Crisis V7 MEGA: sufinanciranje_sport + panel + CRM auth
DB: - pgz_sport.sufinanciranje_sport.je_klub flag (RSS programi/totals false) - pgz_sport.sufinanciranje_sport.klub_id matched Endpoints: - /v2/potpore/by-year: samo_klubovi=True default + davatelj filter Frontend: - sport2.html PANEL FORCE HIDE CSS (right:-100vw default) - crm_v2.html: redirect to /login only on actual 401, not on page load
This commit is contained in:
+264
-48
@@ -134,6 +134,17 @@ table tbody tr.no-click:hover{background:transparent}
|
||||
.btn.primary{background:linear-gradient(135deg,var(--pgz-blue),var(--pgz-blue2));border-color:transparent;color:#fff}
|
||||
.btn.primary:hover{filter:brightness(1.1)}
|
||||
|
||||
/* BUG-E (2026-05-05) — filter-bar above section toolbar */
|
||||
.bugE-bar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:0 0 10px 0;padding:10px 12px;background:linear-gradient(180deg,rgba(20,30,48,.6),rgba(15,22,36,.5));border:1px solid var(--rim);border-left:3px solid var(--pgz-gold);border-radius:6px}
|
||||
.bugE-bar .bugE-lbl{font-size:10px;color:var(--pgz-gold);font-weight:800;letter-spacing:1.4px}
|
||||
.bugE-bar label{font-size:11px;color:var(--t1);display:flex;align-items:center;gap:6px;cursor:pointer;user-select:none}
|
||||
.bugE-bar label small{color:var(--t2);font-weight:400}
|
||||
.bugE-bar input[type=checkbox]{accent-color:var(--pgz-gold)}
|
||||
.bugE-bar input[type=number]{background:var(--bg2);border:1px solid var(--rim);border-radius:4px;padding:5px 8px;color:var(--t1);font-size:12px}
|
||||
.bugE-bar .btn{padding:6px 12px;font-size:11px}
|
||||
.bugE-bar .bugE-cnt{margin-left:auto;font-size:11px;color:var(--t2);font-weight:600;letter-spacing:.5px}
|
||||
.bugE-bar .bugE-cnt strong{color:var(--pgz-gold)}
|
||||
|
||||
.toggle{display:inline-flex;background:var(--bg2);border:1px solid var(--rim);border-radius:5px;overflow:hidden}
|
||||
.toggle button{background:transparent;border:0;padding:6px 12px;color:var(--t2);font-size:11px;font-weight:600;cursor:pointer}
|
||||
.toggle button.active{background:var(--pgz-blue);color:#fff}
|
||||
@@ -161,8 +172,8 @@ a.tag:hover,.tag[onclick]:hover{transform:translateY(-1px);filter:brightness(1.1
|
||||
.tag.rd{background:var(--red);color:#fff}
|
||||
.tag.am{background:var(--amber);color:var(--bg0)}
|
||||
|
||||
#panel{position:fixed;top:0;right:-620px;width:600px;max-width:96vw;height:100vh;background:var(--bg1);border-left:1px solid var(--rim);z-index:200;transition:right .25s ease;display:flex;flex-direction:column;box-shadow:-8px 0 30px rgba(0,0,0,.5)}
|
||||
#panel.open{right:0}
|
||||
#panel{position:fixed;top:0;right:0;width:600px;max-width:96vw;height:100vh;background:var(--bg1);border-left:1px solid var(--rim);z-index:200;transform:translateX(100%);visibility:hidden;transition:transform .25s ease,visibility 0s linear .25s;display:flex;flex-direction:column;box-shadow:-8px 0 30px rgba(0,0,0,.5)}
|
||||
#panel.open{transform:translateX(0);visibility:visible;transition:transform .25s ease,visibility 0s linear 0s}
|
||||
#panel-hdr{padding:14px 16px;border-bottom:1px solid var(--rim);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;background:var(--bg2)}
|
||||
#panel-hdr-t{font-size:14px;font-weight:700;color:var(--t0)}
|
||||
#panel-x{cursor:pointer;font-size:22px;color:var(--t4);width:30px;height:30px;display:flex;align-items:center;justify-content:center;border-radius:5px;transition:all .15s}
|
||||
@@ -284,7 +295,7 @@ a.tag:hover,.tag[onclick]:hover{transform:translateY(-1px);filter:brightness(1.1
|
||||
.table-container, .card { overflow-x: auto; }
|
||||
|
||||
/* Drill-down panel full-width */
|
||||
#panel { width: 100vw !important; max-width: 100vw !important; right: -100vw !important; }
|
||||
#panel { width: 100vw !important; max-width: 100vw !important; right: 0 !important; }
|
||||
#panel.open { right: 0 !important; }
|
||||
|
||||
/* Buttons */
|
||||
@@ -314,6 +325,12 @@ a.tag:hover,.tag[onclick]:hover{transform:translateY(-1px);filter:brightness(1.1
|
||||
/* HNS karijera tabela full-width */
|
||||
#panel table, #dpanel table { width: 100%; font-size: 12px; }
|
||||
#panel .hns-stats td { padding: 4px 6px; }
|
||||
|
||||
/* PANEL FORCE HIDE (CRISIS V7) — uvijek skriven dok nije .open */
|
||||
#panel:not(.open) { right: -100vw !important; transform: translateX(0) !important; }
|
||||
#panel.open { right: 0 !important; }
|
||||
#panel-overlay:not(.open) { display: none !important; }
|
||||
#panel-overlay.open { display: block !important; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="/static/shared/sidebar.css">
|
||||
<script src="/static/shared/sidebar.js" defer data-active="dashboard"></script>
|
||||
@@ -406,6 +423,83 @@ const _state = {section:'dashboard', viewSavezi:'card', viewKlubovi:'card', view
|
||||
const _sort = {savezi:null, klubovi:null, sportasi:null, objekti:null, manifestacije:null, financije:null};
|
||||
let _proracunChart=null, _financijeChart=null;
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════
|
||||
// BUG-E (2026-05-05) — explicit filter-bar state per section
|
||||
// Author: Damir Radulić (dradulic@outlook.com / damir@rinet.one)
|
||||
// Defaults match constitution: financirani=true + u-godišnjaku=true.
|
||||
// User can uncheck either checkbox to broaden the result set.
|
||||
// ════════════════════════════════════════════════════════════════════
|
||||
const _filters = {
|
||||
klubovi: { financirani: true, godisnjak: true, hns_roster: false, total: 0 },
|
||||
sportasi: { priority: true, hns_profil: false, godina_od: null, godina_do: null, total: 0 },
|
||||
savezi: { financirani: true, total: 0 }
|
||||
};
|
||||
function _filtersDefaults(sec){
|
||||
if(sec==='klubovi') return { financirani:true, godisnjak:true, hns_roster:false };
|
||||
if(sec==='sportasi') return { priority:true, hns_profil:false, godina_od:null, godina_do:null };
|
||||
if(sec==='savezi') return { financirani:true };
|
||||
return {};
|
||||
}
|
||||
function _filtersReset(sec){
|
||||
Object.assign(_filters[sec], _filtersDefaults(sec));
|
||||
_filtersApply(sec);
|
||||
}
|
||||
function _filtersApply(sec){
|
||||
if(sec==='klubovi') { _cache.klubovi = null; loadKlubovi(); }
|
||||
if(sec==='sportasi') { _cache.clanovi = null; loadSportasi(); }
|
||||
if(sec==='savezi') { _cache.savezi = null; loadSavezi(); }
|
||||
}
|
||||
function _filtersBar(sec){
|
||||
// Returns HTML for the BUG-E filter-bar above the existing toolbar.
|
||||
const f = _filters[sec] || {};
|
||||
const cnt = '<span class="bugE-cnt" id="bugE-cnt-'+sec+'">Prikazano: '
|
||||
+ (f.shown||0) + ' od ' + (f.total||0) + '</span>';
|
||||
if(sec==='klubovi'){
|
||||
return `
|
||||
<div class="bugE-bar">
|
||||
<span class="bugE-lbl">FILTER:</span>
|
||||
<label><input type="checkbox" ${f.financirani?'checked':''} onchange="_filters.klubovi.financirani=this.checked"> Samo financirani <small>(PGŽ + RSS + Grad Rijeka)</small></label>
|
||||
<label><input type="checkbox" ${f.godisnjak?'checked':''} onchange="_filters.klubovi.godisnjak=this.checked"> U godišnjaku</label>
|
||||
<label><input type="checkbox" ${f.hns_roster?'checked':''} onchange="_filters.klubovi.hns_roster=this.checked"> Ima HNS roster</label>
|
||||
<button class="btn primary" onclick="_filtersApply('klubovi')">Primijeni</button>
|
||||
<button class="btn" onclick="_filtersReset('klubovi')">Reset</button>
|
||||
${cnt}
|
||||
</div>`;
|
||||
}
|
||||
if(sec==='sportasi'){
|
||||
return `
|
||||
<div class="bugE-bar">
|
||||
<span class="bugE-lbl">FILTER:</span>
|
||||
<label><input type="checkbox" ${f.priority?'checked':''} onchange="_filters.sportasi.priority=this.checked"> Samo iz priority kluba</label>
|
||||
<label><input type="checkbox" ${f.hns_profil?'checked':''} onchange="_filters.sportasi.hns_profil=this.checked"> Ima HNS profil</label>
|
||||
<label class="bugE-range">Godina rođ. od: <input type="number" min="1900" max="2030" value="${f.godina_od||''}" placeholder="—" onchange="_filters.sportasi.godina_od=this.value?parseInt(this.value,10):null" style="width:90px"></label>
|
||||
<label class="bugE-range">do: <input type="number" min="1900" max="2030" value="${f.godina_do||''}" placeholder="—" onchange="_filters.sportasi.godina_do=this.value?parseInt(this.value,10):null" style="width:90px"></label>
|
||||
<button class="btn primary" onclick="_filtersApply('sportasi')">Primijeni</button>
|
||||
<button class="btn" onclick="_filtersReset('sportasi')">Reset</button>
|
||||
${cnt}
|
||||
</div>`;
|
||||
}
|
||||
if(sec==='savezi'){
|
||||
return `
|
||||
<div class="bugE-bar">
|
||||
<span class="bugE-lbl">FILTER:</span>
|
||||
<label><input type="checkbox" ${f.financirani?'checked':''} onchange="_filters.savezi.financirani=this.checked"> Samo financirani</label>
|
||||
<button class="btn primary" onclick="_filtersApply('savezi')">Primijeni</button>
|
||||
<button class="btn" onclick="_filtersReset('savezi')">Reset</button>
|
||||
${cnt}
|
||||
</div>`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
function _filtersUpdateCount(sec, shown){
|
||||
_filters[sec].shown = shown;
|
||||
const el = document.getElementById('bugE-cnt-'+sec);
|
||||
if(el) el.textContent = 'Prikazano: '+shown+' od '+(_filters[sec].total||shown);
|
||||
}
|
||||
window._filters = _filters;
|
||||
window._filtersReset = _filtersReset;
|
||||
window._filtersApply = _filtersApply;
|
||||
|
||||
// === PGŽ priority filter (SUB6) — global helper, works across Klubovi/Savezi/Sportaši ===
|
||||
window._pgz_filter_priority = window._pgz_filter_priority || false;
|
||||
window.togglePGZFilter = function(section){
|
||||
@@ -1240,13 +1334,16 @@ async function loadSavezi(){
|
||||
const root = $('#pg-savezi');
|
||||
if(!_cache.savezi){
|
||||
root.innerHTML = '<div class="loading">Učitavanje saveza…</div>';
|
||||
// PGŽ filter: switch to v2 priority-sort endpoint (only=true returns just PGŽ-relevant savezi)
|
||||
const url = window._pgz_filter_priority
|
||||
// BUG-E (2026-05-05): explicit filter — when financirani=true → priority-sort?only=true
|
||||
const f = _filters.savezi;
|
||||
const useOnly = f.financirani || window._pgz_filter_priority;
|
||||
const url = useOnly
|
||||
? '/v2/savezi/priority-sort?only=true&limit=500'
|
||||
: '/savezi?limit=250';
|
||||
: '/v2/savezi/priority-sort?only=false&limit=500';
|
||||
const d = await api(url);
|
||||
if(!d){ root.innerHTML='<div class="empty">Greška pri dohvatu</div>'; return; }
|
||||
_cache.savezi = d.rows || [];
|
||||
_filters.savezi.total = (d.rows||[]).length;
|
||||
}
|
||||
renderSaveziShell();
|
||||
applySaveziFilter();
|
||||
@@ -1255,6 +1352,7 @@ function renderSaveziShell(){
|
||||
const root = $('#pg-savezi');
|
||||
const sports = Array.from(new Set((_cache.savezi||[]).map(s=>s.sport).filter(Boolean))).sort();
|
||||
root.innerHTML = `
|
||||
${_filtersBar('savezi')}
|
||||
<div class="toolbar">
|
||||
<input type="search" id="sav-q" placeholder="🔍 Pretraži savez…">
|
||||
<select id="sav-sport"><option value="">Svi sportovi</option>${sports.map(s=>'<option value="'+esc(s)+'">'+esc(s)+'</option>').join('')}</select>
|
||||
@@ -1376,7 +1474,7 @@ async function openSavez(id){
|
||||
${klubovi.length ? `<div style="overflow-x:auto;max-height:400px;overflow-y:auto"><table>
|
||||
<thead><tr><th>Klub</th><th>Razina</th><th>Grad</th></tr></thead>
|
||||
<tbody>${klubovi.slice(0,100).map(k => `
|
||||
<tr onclick="closePanel();setTimeout(()=>openKlub(${k.id}),250)">
|
||||
<tr onclick="panelDrill(openKlub, ${k.id})">
|
||||
<td>${esc(k.klub||k.sport||'(bez naziva)')}${k.nositelj_kvalitete?' <span class="tag gd">N.K.</span>':''}</td>
|
||||
<td>${txt(k.razina,'')}</td>
|
||||
<td>${txt(k.grad)}</td>
|
||||
@@ -1397,14 +1495,27 @@ async function loadKlubovi(){
|
||||
const root = $('#pg-klubovi');
|
||||
if(!_cache.klubovi){
|
||||
root.innerHTML = '<div class="loading">Učitavanje klubova…</div>';
|
||||
// /api/klubovi already returns priority/financiran/godisnjak flags.
|
||||
// When PGŽ filter is on, ask backend to only return priority klubs.
|
||||
const url = window._pgz_filter_priority
|
||||
? '/klubovi?kategorija=priority&limit=2500'
|
||||
: '/klubovi?limit=2500';
|
||||
const d = await api(url);
|
||||
// BUG-E (2026-05-05): build /api/klubovi URL from explicit _filters.klubovi state.
|
||||
// Defaults: financirani=true + godisnjak=true. When BOTH off → load all.
|
||||
const f = _filters.klubovi;
|
||||
const qs = new URLSearchParams();
|
||||
qs.set('limit','2500');
|
||||
qs.set('sort','financiran'); qs.set('order','desc'); // sort by potpore DESC (financiran flag)
|
||||
// financirani + godisnjak combined with kategorija=priority logic:
|
||||
if(f.financirani && f.godisnjak){
|
||||
qs.set('kategorija','priority'); // OR semantics → priority = financiran OR godišnjak
|
||||
} else if(f.financirani){
|
||||
qs.set('financiran','true');
|
||||
} else if(f.godisnjak){
|
||||
qs.set('godisnjak','true');
|
||||
}
|
||||
if(f.hns_roster) qs.set('samo_hns_roster','true');
|
||||
// Legacy global toggle still respected (if user clicks the old PGŽ button).
|
||||
if(window._pgz_filter_priority && !qs.has('kategorija')) qs.set('kategorija','priority');
|
||||
const d = await api('/klubovi?'+qs.toString());
|
||||
if(!d){ root.innerHTML='<div class="empty">Greška pri dohvatu</div>'; return; }
|
||||
_cache.klubovi = d.rows || [];
|
||||
_filters.klubovi.total = (d.rows||[]).length;
|
||||
}
|
||||
renderKluboviShell();
|
||||
applyKluboviFilter();
|
||||
@@ -1414,6 +1525,7 @@ function renderKluboviShell(){
|
||||
const sports = Array.from(new Set((_cache.klubovi||[]).map(k=>k.sport).filter(Boolean))).sort().slice(0,80);
|
||||
const grads = Array.from(new Set((_cache.klubovi||[]).map(k=>k.grad).filter(Boolean))).sort();
|
||||
root.innerHTML = `
|
||||
${_filtersBar('klubovi')}
|
||||
<div class="toolbar">
|
||||
<input type="search" id="kl-q" placeholder="🔍 Pretraži klub…">
|
||||
<select id="kl-sport"><option value="">Svi sportovi</option>${sports.map(s=>'<option value="'+esc(s)+'">'+esc(s)+'</option>').join('')}</select>
|
||||
@@ -1464,6 +1576,8 @@ function applyKluboviFilter(){
|
||||
else if(kat==='financiran') rows = rows.filter(k => k.financiran);
|
||||
else if(kat==='godisnjak') rows = rows.filter(k => k.godisnjak);
|
||||
if(_sort.klubovi) rows = sortRows(rows, _sort.klubovi.key, _sort.klubovi.dir);
|
||||
// BUG-E: live count + total
|
||||
_filtersUpdateCount('klubovi', rows.length);
|
||||
$('#kl-cnt').textContent = rows.length+' klubova';
|
||||
const top = rows.slice(0, 300);
|
||||
$('#kl-out').innerHTML = _state.viewKlubovi==='card' ? renderKluboviGrid(top) : renderKluboviTable(top);
|
||||
@@ -1638,7 +1752,7 @@ async function openKlub(id){
|
||||
${clanovi.length ? `<div style="overflow-x:auto;max-height:500px;overflow-y:auto"><table>
|
||||
<thead><tr><th>Sportaš</th><th>Spol</th><th>Pozicija</th><th>Kategorija</th><th>Tagovi</th></tr></thead>
|
||||
<tbody>${clanovi.map(c => `
|
||||
<tr onclick="closePanel();setTimeout(()=>openSportas(${c.id}),250)">
|
||||
<tr onclick="panelDrill(openSportas, ${c.id})">
|
||||
<td><b>${esc(c.ime||'')} ${esc(c.prezime||'')}</b></td>
|
||||
<td>${txt(c.spol)}</td>
|
||||
<td>${txt(c.pozicija)}</td>
|
||||
@@ -1664,7 +1778,7 @@ async function openKlub(id){
|
||||
<details style="margin-bottom:8px" ${groups[kat].length<=12?'open':''}>
|
||||
<summary style="cursor:pointer;padding:8px;background:rgba(255,255,255,.04);border-radius:6px"><b>${esc(kat)}</b> · ${groups[kat].length} igrač${groups[kat].length===1?'':'a'}</summary>
|
||||
<table style="margin-top:6px"><tbody>${groups[kat].map(c => `
|
||||
<tr onclick="closePanel();setTimeout(()=>openSportas(${c.id}),250)">
|
||||
<tr onclick="panelDrill(openSportas, ${c.id})">
|
||||
<td><b>${esc(c.ime||'')} ${esc(c.prezime||'')}</b></td>
|
||||
<td>${txt(c.spol)}</td>
|
||||
<td>${txt(c.pozicija)}</td>
|
||||
@@ -2030,7 +2144,7 @@ async function openSportas(id){
|
||||
<div class="pp-meta">
|
||||
${d.sport?'<a class="link-chip" onclick="filterSportasiBy("sport","'+esc(d.sport)+'")">'+esc(d.sport)+'</a>':'—'} ·
|
||||
${txt(d.pozicija,'')} ·
|
||||
${d.klub_id ? '<a class="link-chip" onclick="closePanel();setTimeout(()=>openKlub('+d.klub_id+'),250)"><b>'+esc(d.klub_naziv_full||d.klub_naziv_godisnjak||'—')+'</b></a>' : '<b>'+esc(d.klub_naziv_full||d.klub_naziv_godisnjak||'—')+'</b>'}
|
||||
${d.klub_id ? '<a class="link-chip" onclick="panelDrill(openKlub,'+d.klub_id+')"><b>'+esc(d.klub_naziv_full||d.klub_naziv_godisnjak||'—')+'</b></a>' : '<b>'+esc(d.klub_naziv_full||d.klub_naziv_godisnjak||'—')+'</b>'}
|
||||
</div>
|
||||
<div class="pp-meta">
|
||||
${dob ? '<a class="link-chip" onclick="filterSportasiByYear("'+esc((dob||'').slice(0,4))+'")">📅 '+fmtDate(dob)+'</a>' : '📅 —'}
|
||||
@@ -2050,11 +2164,8 @@ async function openSportas(id){
|
||||
${d.pozicija?'<span class="pp-bio-chip"><b>'+esc(d.pozicija)+'</b></span>':''}
|
||||
${d.broj_dresa?'<span class="pp-bio-chip">#<b>'+esc(d.broj_dresa)+'</b></span>':''}
|
||||
</div>
|
||||
<div class="pp-links">
|
||||
${hnsUrl?'<a class="pp-link hns" href="'+esc(hnsUrl)+'" target="_blank" rel="noopener">⚽ HNS profil ↗</a>':''}
|
||||
<a class="pp-link gg" href="${esc(ggUrl)}" target="_blank" rel="noopener">🔍 Google</a>
|
||||
<a class="pp-link wiki" href="${esc(wikiUrl)}" target="_blank" rel="noopener">📖 Wikipedia</a>
|
||||
</div>
|
||||
<!-- BUG-F (2026-05-05): external links moved to dedicated 🔗 Linkovi tab -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2067,14 +2178,15 @@ async function openSportas(id){
|
||||
<div class="pp-stat"><div class="v">${fmtNum(stats.sezone_aktivne||sezone.length)}</div><div class="l">Sezona</div></div>
|
||||
</div>
|
||||
|
||||
<!-- HNS-3 (2026-05-05) — 3 explicit tabs: HNS Karijera / Utakmice (last 30) / Profil -->
|
||||
<!-- BUG-F (2026-05-05) — 4 explicit tabs: Profil / HNS Karijera / Utakmice (last 30) / Linkovi -->
|
||||
<div class="tabs">
|
||||
<div class="tab active" onclick="switchPlayerTab(this,'p-sez')">🏆 HNS Karijera (${sezone.length})</div>
|
||||
<div class="tab active" onclick="switchPlayerTab(this,'p-prof')">👤 Profil</div>
|
||||
<div class="tab" onclick="switchPlayerTab(this,'p-sez')">🏆 HNS Karijera (${sezone.length})</div>
|
||||
<div class="tab" onclick="switchPlayerTab(this,'p-utak')">📅 Utakmice (poslj. ${Math.min(utakmice.length,30)})</div>
|
||||
<div class="tab" onclick="switchPlayerTab(this,'p-prof')">👤 Profil</div>
|
||||
<div class="tab" onclick="switchPlayerTab(this,'p-link')">🔗 Linkovi</div>
|
||||
</div>
|
||||
|
||||
<div id="p-sez" class="ptab">
|
||||
<div id="p-sez" class="ptab" style="display:none">
|
||||
<div class="pp-section-h">🏆 HNS Karijera <span class="cnt">${sezone.length} sezon${sezone.length===1?'a':(sezone.length<5&&sezone.length>1?'e':'a')}</span></div>
|
||||
${sezone.length ? `<div style="overflow-x:auto"><table>
|
||||
<thead><tr><th>Sezona</th><th>Klub</th><th>Natjecanje</th><th class="num">Nastupi</th><th class="num">Golovi</th><th class="num">Asis.</th><th class="num">Žuti</th><th class="num">Crv.</th><th class="num">Min.</th><th></th></tr></thead>
|
||||
@@ -2124,7 +2236,7 @@ async function openSportas(id){
|
||||
</table></div>` : '<div class="empty">Nema podataka o utakmicama</div>'}
|
||||
</div>
|
||||
|
||||
<div id="p-prof" class="ptab" style="display:none">
|
||||
<div id="p-prof" class="ptab">
|
||||
<div class="pp-section-h">👤 Profil <span class="cnt">${esc(d.ime||'')} ${esc(d.prezime||'')}</span></div>
|
||||
|
||||
<!-- Top: name + dres + active club + HNS deep link -->
|
||||
@@ -2136,7 +2248,7 @@ async function openSportas(id){
|
||||
${(d.mjesto_rodjenja||d.mjesto_rodenja)?' · '+esc(d.mjesto_rodjenja||d.mjesto_rodenja):''}
|
||||
</div>
|
||||
<div class="prof-club">
|
||||
${d.klub_id ? '<a class="link-chip" onclick="closePanel();setTimeout(()=>openKlub('+d.klub_id+'),250)">🏟️ '+esc(d.klub_naziv_full||d.klub_naziv||d.klub_naziv_godisnjak||'—')+'</a>' : '🏟️ '+esc(d.klub_naziv_full||d.klub_naziv||d.klub_naziv_godisnjak||'—')}
|
||||
${d.klub_id ? '<a class="link-chip" onclick="panelDrill(openKlub,'+d.klub_id+')">🏟️ '+esc(d.klub_naziv_full||d.klub_naziv||d.klub_naziv_godisnjak||'—')+'</a>' : '🏟️ '+esc(d.klub_naziv_full||d.klub_naziv||d.klub_naziv_godisnjak||'—')}
|
||||
${d.aktivan?'<span class="tag gr" style="margin-left:8px">AKTIVAN</span>':'<span class="tag rd" style="margin-left:8px">NEAKTIVAN</span>'}
|
||||
</div>
|
||||
${hnsUrl?'<div style="margin-top:10px"><a class="pp-link hns" href="'+esc(hnsUrl)+'" target="_blank" rel="noopener">⚽ HNS Semafor profil ↗</a></div>':''}
|
||||
@@ -2169,7 +2281,7 @@ async function openSportas(id){
|
||||
<tr class="no-click">
|
||||
<td><b>${esc(k.sezona||'—')}</b></td>
|
||||
<td><span class="tag b">${esc(k.kategorija||'—')}</span></td>
|
||||
<td>${k.klub_id ? '<a class="link-chip" onclick="closePanel();setTimeout(()=>openKlub('+k.klub_id+'),250)">'+esc(k.klub_naziv||('Klub #'+k.klub_id))+'</a>' : (k.klub_naziv?esc(k.klub_naziv):'—')}</td>
|
||||
<td>${k.klub_id ? '<a class="link-chip" onclick="panelDrill(openKlub,'+k.klub_id+')">'+esc(k.klub_naziv||('Klub #'+k.klub_id))+'</a>' : (k.klub_naziv?esc(k.klub_naziv):'—')}</td>
|
||||
<td>${esc(k.source||'—')}</td>
|
||||
<td>${k.source_url?'<a href="'+esc(k.source_url)+'" target="_blank" rel="noopener">↗</a>':''}</td>
|
||||
</tr>`).join('')}
|
||||
@@ -2196,6 +2308,19 @@ async function openSportas(id){
|
||||
</table></div>` : ''}
|
||||
</div>
|
||||
|
||||
<!-- BUG-F (2026-05-05) — 🔗 Linkovi tab: external profile lookups -->
|
||||
<div id="p-link" class="ptab" style="display:none">
|
||||
<div class="pp-section-h">🔗 Linkovi <span class="cnt">${esc(fullName||'sportaš')}</span></div>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:8px">
|
||||
${hnsUrl
|
||||
? `<a class="pp-link hns" href="${esc(hnsUrl)}" target="_blank" rel="noopener" style="padding:18px;font-size:14px;justify-content:space-between"><span>⚽ HNS Semafor profil</span><span style="font-family:var(--mono);color:var(--t3);font-size:11px">#${esc(hnsId||'')} →</span></a>`
|
||||
: `<div class="pp-link" style="padding:18px;font-size:14px;opacity:.5;cursor:not-allowed">⚽ HNS profil <span style="font-size:11px;color:var(--t3);margin-left:auto">nije povezan</span></div>`}
|
||||
<a class="pp-link gg" href="${esc(ggUrl)}" target="_blank" rel="noopener" style="padding:18px;font-size:14px;justify-content:space-between"><span>🔍 Google pretraga</span><span style="color:var(--t3);font-size:11px">→</span></a>
|
||||
<a class="pp-link wiki" href="${esc(wikiUrl)}" target="_blank" rel="noopener" style="padding:18px;font-size:14px;justify-content:space-between"><span>📖 Wikipedia</span><span style="color:var(--t3);font-size:11px">→</span></a>
|
||||
</div>
|
||||
${hnsId ? `<div style="margin-top:14px;padding:10px 12px;background:var(--bg2);border:1px solid var(--rim);border-radius:5px;font-size:11.5px;color:var(--t2);font-family:var(--mono)">HNS ID: <b style="color:var(--pgz-gold)">${esc(hnsId)}</b> · slug: <span style="color:var(--t1)">${esc(slug||'—')}</span></div>` : ''}
|
||||
</div>
|
||||
|
||||
${enrichBlock('sportas', d.id)}
|
||||
`;
|
||||
openPanel('Sportaš · '+(d.ime||'')+' '+(d.prezime||''), html);
|
||||
@@ -3315,8 +3440,8 @@ function renderAlertPanel(a){
|
||||
<div class="card-h"><div class="card-t">🔗 Povezani entiteti</div></div>
|
||||
<table>
|
||||
<tbody>
|
||||
${a.klub_id ? '<tr onclick="closePanel();setTimeout(()=>openKlub('+a.klub_id+'),250)"><td><span class="tag b">Klub</span></td><td><b>Klub #'+a.klub_id+'</b></td><td>Klikni za detalje →</td></tr>' : ''}
|
||||
${a.clan_id ? '<tr onclick="closePanel();setTimeout(()=>openSportas('+a.clan_id+'),250)"><td><span class="tag b">Sportaš</span></td><td><b>Sportaš #'+a.clan_id+'</b></td><td>Klikni za profil →</td></tr>' : ''}
|
||||
${a.klub_id ? '<tr onclick="panelDrill(openKlub,'+a.klub_id+')"><td><span class="tag b">Klub</span></td><td><b>Klub #'+a.klub_id+'</b></td><td>Klikni za detalje →</td></tr>' : ''}
|
||||
${a.clan_id ? '<tr onclick="panelDrill(openSportas,'+a.clan_id+')"><td><span class="tag b">Sportaš</span></td><td><b>Sportaš #'+a.clan_id+'</b></td><td>Klikni za profil →</td></tr>' : ''}
|
||||
</tbody>
|
||||
</table>
|
||||
${(!a.klub_id && !a.clan_id) ? '<div class="empty" style="padding:14px">Nema povezanih entiteta u alarmu</div>' : ''}
|
||||
@@ -3455,32 +3580,123 @@ window.toggleSportasHNS = function(){
|
||||
if(typeof loadSportasi === 'function') loadSportasi();
|
||||
};
|
||||
|
||||
// PANEL HISTORY STACK + NATRAG (CRISIS V7)
|
||||
// PANEL HISTORY STACK + NATRAG (CRISIS V7 / BUG-B opener-based)
|
||||
// Each entry: {opener: <fn name>, args: [...]} — panelBack re-runs the previous opener.
|
||||
window._panelHistory = [];
|
||||
window._panelDrilling = false; // suppress root-clear while drilling
|
||||
window._panelSuppressPush = false; // suppress re-push when re-running for back nav
|
||||
window._panelOpenerMap = {}; // name -> fn
|
||||
|
||||
window.pushPanelState = function(title, htmlContent, callback){
|
||||
// callback may be null — to render this state again later
|
||||
window._panelHistory.push({title, htmlContent, callback});
|
||||
const back = document.getElementById('panel-back');
|
||||
if(back) back.style.display = window._panelHistory.length > 1 ? 'inline-flex' : 'none';
|
||||
window._registerOpener = function(fn){
|
||||
if(typeof fn !== 'function' || !fn.name) return null;
|
||||
window._panelOpenerMap[fn.name] = fn;
|
||||
return fn.name;
|
||||
};
|
||||
|
||||
window._updateBackBtn = function(){
|
||||
const back = document.getElementById('panel-back');
|
||||
if(back) back.style.display = (window._panelHistory.length > 1) ? 'inline-flex' : 'none';
|
||||
};
|
||||
|
||||
// Push opener+args; record in browser history too (for browser back button)
|
||||
window.pushPanelState = function(opener, args){
|
||||
if(window._panelSuppressPush) return;
|
||||
const name = (typeof opener === 'function') ? window._registerOpener(opener) : (typeof opener === 'string' ? opener : null);
|
||||
if(!name) return;
|
||||
const top = window._panelHistory[window._panelHistory.length - 1];
|
||||
const sig = name + ':' + JSON.stringify(args || []);
|
||||
if(top && (top.opener + ':' + JSON.stringify(top.args || [])) === sig) return;
|
||||
window._panelHistory.push({opener: name, args: args || []});
|
||||
try { history.pushState({pgzPanel: true, depth: window._panelHistory.length}, '', location.href); } catch(e) {}
|
||||
window._updateBackBtn();
|
||||
};
|
||||
|
||||
// panelDrill — push state then run opener (no panel close in between)
|
||||
window.panelDrill = function(opener, ...args){
|
||||
window._panelDrilling = true;
|
||||
try {
|
||||
window.pushPanelState(opener, args);
|
||||
const r = opener.apply(null, args);
|
||||
if(r && typeof r.then === 'function') return r.finally(() => { window._panelDrilling = false; });
|
||||
return r;
|
||||
} finally {
|
||||
window._panelDrilling = false;
|
||||
}
|
||||
};
|
||||
|
||||
// panelOpen — root entry: clear history then drill
|
||||
window.panelOpen = function(opener, ...args){
|
||||
window._panelHistory = [];
|
||||
return window.panelDrill(opener, ...args);
|
||||
};
|
||||
|
||||
// panelBack — pop current, re-run previous opener
|
||||
window.panelBack = function(){
|
||||
if(window._panelHistory.length <= 1){ closePanel(); return; }
|
||||
// Remove current state
|
||||
if(window._panelHistory.length <= 1){ window.closePanel(); return; }
|
||||
window._panelHistory.pop();
|
||||
// Restore previous
|
||||
const prev = window._panelHistory[window._panelHistory.length - 1];
|
||||
const t = document.getElementById('panel-hdr-t');
|
||||
const b = document.getElementById('panel-body');
|
||||
if(t) t.textContent = prev.title;
|
||||
if(b) b.innerHTML = prev.htmlContent;
|
||||
if(typeof prev.callback === 'function') prev.callback();
|
||||
const back = document.getElementById('panel-back');
|
||||
if(back) back.style.display = window._panelHistory.length > 1 ? 'inline-flex' : 'none';
|
||||
const fn = window._panelOpenerMap[prev.opener];
|
||||
if(typeof fn !== 'function'){ window.closePanel(); return; }
|
||||
window._panelSuppressPush = true;
|
||||
window._panelDrilling = true;
|
||||
try { fn.apply(null, prev.args || []); } catch(e) { console.error('panelBack', e); }
|
||||
setTimeout(() => {
|
||||
window._panelSuppressPush = false;
|
||||
window._panelDrilling = false;
|
||||
window._updateBackBtn();
|
||||
}, 60);
|
||||
window._updateBackBtn();
|
||||
};
|
||||
|
||||
// Override closePanel — clear history
|
||||
// Browser back button → mirror panelBack while panel is open
|
||||
window.addEventListener('popstate', function(){
|
||||
const panel = document.getElementById('panel');
|
||||
if(!panel || !panel.classList.contains('open')) return;
|
||||
if(window._panelHistory.length > 1){
|
||||
window._panelSuppressPush = true;
|
||||
window._panelDrilling = true;
|
||||
window._panelHistory.pop();
|
||||
const prev = window._panelHistory[window._panelHistory.length - 1];
|
||||
const fn = window._panelOpenerMap[prev.opener];
|
||||
if(typeof fn === 'function') fn.apply(null, prev.args || []);
|
||||
setTimeout(() => {
|
||||
window._panelSuppressPush = false;
|
||||
window._panelDrilling = false;
|
||||
window._updateBackBtn();
|
||||
}, 60);
|
||||
window._updateBackBtn();
|
||||
} else {
|
||||
window.closePanel();
|
||||
}
|
||||
});
|
||||
|
||||
// Wrap root open* functions: a non-drill call clears history and registers itself as root.
|
||||
window._wrapOpener = function(name){
|
||||
const orig = window[name];
|
||||
if(typeof orig !== 'function' || orig.__pgzWrapped) return;
|
||||
window._registerOpener(orig);
|
||||
const wrapped = function(...args){
|
||||
if(!window._panelDrilling && !window._panelSuppressPush){
|
||||
window._panelHistory = [];
|
||||
window._panelHistory.push({opener: name, args});
|
||||
try { history.pushState({pgzPanel: true, depth: 1}, '', location.href); } catch(e) {}
|
||||
window._updateBackBtn();
|
||||
}
|
||||
return orig.apply(this, args);
|
||||
};
|
||||
wrapped.__pgzWrapped = true;
|
||||
try { Object.defineProperty(wrapped, 'name', {value: name, configurable: true}); } catch(e) {}
|
||||
window[name] = wrapped;
|
||||
window._panelOpenerMap[name] = wrapped;
|
||||
};
|
||||
|
||||
// Wrap all known root openers (idempotent)
|
||||
['openSavez','openKlub','openSportas','openObjekt','openManif',
|
||||
'openPrimateljDetail','openProracunDrill','openSavezByName',
|
||||
'openMrezaNode','openForensicDetail','openOIB']
|
||||
.forEach(function(n){ try { window._wrapOpener(n); } catch(e) {} });
|
||||
|
||||
// Override closePanel — X button always returns to root: clear history & back btn
|
||||
window._origClosePanel = window.closePanel;
|
||||
window.closePanel = function(){
|
||||
window._panelHistory = [];
|
||||
@@ -3489,7 +3705,7 @@ window.closePanel = function(){
|
||||
const p = document.getElementById('panel');
|
||||
if(p) p.classList.remove('open');
|
||||
const ov = document.getElementById('panel-overlay');
|
||||
if(ov) ov.style.display = 'none';
|
||||
if(ov){ ov.classList.remove('open'); ov.style.removeProperty('display'); }
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user