6-sub sprint: Dokumenti+HNS profil+Admin+ERP+CRM+PGŽ filter
SUB1 Dokumenti: pgz:dokumenti SECTIONS handler u app.html (klikabilan grid 19 godišnjaka, PDF stream) SUB2 HNS profil: sport2.html drill-down — bio-chips (visina/težina/noga/poz/dres) + HNS deep + Google + Wiki + 🏆 Karijera/📅 Utakmice tabovi (Josip Zec id=449: 257 nast/182 gol/15 sez) SUB3 Admin Users: sidebar.js href fix /admin/users → /sport/admin/users + razriješen audit ID konflikt SUB4 ERP Full: 5 novih endpointa (invoice-uploads, racuni/ulazni/{rid}/uploads, expense-reports, putni-nalog-racuni, payments) + 3 nova taba (📎 Uploads/OCR, ✈ Putni, 💰 Plaćanja) + inline stavke drill-down + sidebar entry SUB5 CRM Salesforce-Lite: dodan crm_v2 sidebar entry (router 956 linija već mounted) SUB6 PGŽ filter: 2 nova endpointa /api/v2/savezi/priority-sort + /api/v2/clanovi/priority-sort; togglePGZFilter wired u Klubovi/Savezi/Sportaši (sport2.html + app.html); ⭐💰📖 badge prefix; klubovi 1536/1641, savezi 35/246, sportaši 4979/5499 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+94
-15
@@ -1230,47 +1230,62 @@ SECTIONS['pgz:korisnici'] = () => {
|
||||
};
|
||||
|
||||
SECTIONS['pgz:savezi'] = async () => {
|
||||
const d = await api('/savezi') || {rows:[]};
|
||||
const onPGZ = !!window._pgz_filter_priority;
|
||||
const url = onPGZ ? '/v2/savezi/priority-sort?only=true&limit=300' : '/savezi';
|
||||
const d = await api(url) || {rows:[]};
|
||||
const top = (d.rows||[]).slice(0,30);
|
||||
const bp = window.pgzBadgePrefix || (()=> '');
|
||||
const rows = top.map(s => `
|
||||
<tr style="cursor:pointer" onclick="showDetail('savez',${s.id},${JSON.stringify(s.naziv)})">
|
||||
<td><b>${esc(s.naziv)}</b></td>
|
||||
<td><b>${bp(s)}${esc(s.naziv)}</b></td>
|
||||
<td class="num">${fmt(s.broj_klubova||'—')}</td>
|
||||
<td class="num">${fmt(s.broj_sportasa||'—')}</td>
|
||||
<td>${esc(s.predsjednik||'—')}</td>
|
||||
<td><button class="btn sm" onclick="event.stopPropagation();showDetail('savez',${s.id},${JSON.stringify(s.naziv)})">Detalji</button></td>
|
||||
</tr>`).join('');
|
||||
return `<div class="card"><div class="card-h"><div class="card-t">🏅 Savezi PGŽ — top 30 (od ${d.count||246})</div></div>
|
||||
const tb = window.renderPGZToggleBtn ? window.renderPGZToggleBtn() : '';
|
||||
return `<div class="card"><div class="card-h"><div class="card-t">🏅 Savezi PGŽ — top 30 (od ${d.count||246})${onPGZ?' · ⭐ samo PGŽ-relevantni':''}</div></div>
|
||||
${tb}
|
||||
<table><thead><tr><th>Naziv</th><th class="num">Klubovi</th><th class="num">Sportaši</th><th>Predsjednik</th><th></th></tr></thead><tbody>${rows||'<tr><td colspan=5 class="empty">Učitavam...</td></tr>'}</tbody></table>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
SECTIONS['pgz:klubovi'] = async () => {
|
||||
const d = await api('/klubovi?limit=40') || {rows:[]};
|
||||
const rows = (d.rows||[]).slice(0,40).map(k => `
|
||||
<tr style="cursor:pointer" onclick="showDetail('klub',${k.id},${JSON.stringify(k.naziv)})">
|
||||
<td><b>${esc(k.naziv)}</b></td>
|
||||
const onPGZ = !!window._pgz_filter_priority;
|
||||
const url = onPGZ ? '/klubovi?kategorija=priority&limit=80' : '/klubovi?limit=40';
|
||||
const d = await api(url) || {rows:[]};
|
||||
const bp = window.pgzBadgePrefix || (()=> '');
|
||||
const rows = (d.rows||[]).slice(0,80).map(k => `
|
||||
<tr style="cursor:pointer" onclick="showDetail('klub',${k.id},${JSON.stringify(k.naziv||k.klub)})">
|
||||
<td><b>${bp(k)}${esc(k.naziv||k.klub||'—')}</b></td>
|
||||
<td>${esc(k.savez||'—')}</td>
|
||||
<td>${esc(k.grad||'—')}</td>
|
||||
<td class="num">${fmt(k.broj_clanova||'—')}</td>
|
||||
<td>${esc(k.predsjednik||'—')}</td>
|
||||
</tr>`).join('');
|
||||
return `<div class="card"><div class="card-h"><div class="card-t">⬢ Klubovi (${d.count||0})</div></div>
|
||||
const tb = window.renderPGZToggleBtn ? window.renderPGZToggleBtn() : '';
|
||||
return `<div class="card"><div class="card-h"><div class="card-t">⬢ Klubovi (${d.count||0})${onPGZ?' · ⭐ samo PGŽ-prioritet':''}</div></div>
|
||||
${tb}
|
||||
<table><thead><tr><th>Naziv</th><th>Savez</th><th>Grad</th><th class="num">Članova</th><th>Predsjednik</th></tr></thead><tbody>${rows||'<tr><td colspan=5 class="empty">—</td></tr>'}</tbody></table>
|
||||
</div>`;
|
||||
};
|
||||
|
||||
SECTIONS['pgz:sportasi'] = async () => {
|
||||
const d = await api('/clanovi?limit=40') || {rows:[]};
|
||||
const rows = (d.rows||[]).slice(0,40).map(c => `
|
||||
const onPGZ = !!window._pgz_filter_priority;
|
||||
const url = onPGZ ? '/v2/clanovi/priority-sort?only=true&limit=400' : '/clanovi?limit=40';
|
||||
const d = await api(url) || {rows:[]};
|
||||
const bp = window.pgzBadgePrefix || (()=> '');
|
||||
const rows = (d.rows||[]).slice(0,80).map(c => `
|
||||
<tr>
|
||||
<td><b>${esc(c.ime+' '+(c.prezime||''))}</b></td>
|
||||
<td>${esc(c.klub||'—')}</td>
|
||||
<td><b>${bp(c)}${esc((c.ime||'')+' '+(c.prezime||''))}</b></td>
|
||||
<td>${esc(c.klub||c.klub_naziv||c.klub_naziv_godisnjak||'—')}</td>
|
||||
<td>${esc(c.kategorija||'—')}</td>
|
||||
<td>${esc(c.spol||'—')}</td>
|
||||
<td>${esc(c.datum_rodjenja||'—')}</td>
|
||||
<td>${esc(c.datum_rodjenja||c.datum_rodenja||'—')}</td>
|
||||
</tr>`).join('');
|
||||
return `<div class="card"><div class="card-h"><div class="card-t">👤 Sportaši (${d.count||0})</div></div>
|
||||
const tb = window.renderPGZToggleBtn ? window.renderPGZToggleBtn() : '';
|
||||
return `<div class="card"><div class="card-h"><div class="card-t">👤 Sportaši (${d.count||0})${onPGZ?' · ⭐ samo PGŽ-prioritet':''}</div></div>
|
||||
${tb}
|
||||
<table><thead><tr><th>Ime i prezime</th><th>Klub</th><th>Kategorija</th><th>Spol</th><th>Rođen</th></tr></thead><tbody>${rows||'<tr><td colspan=5 class="empty">—</td></tr>'}</tbody></table>
|
||||
</div>`;
|
||||
};
|
||||
@@ -1480,6 +1495,51 @@ SECTIONS['pgz:forenzika'] = () => `
|
||||
</div>`).join('')}
|
||||
</div>`;
|
||||
|
||||
// ─── pgz:dokumenti — knjižnica godišnjaka (in-page) ────────────────────
|
||||
// Prikazuje grid kartica za 18+ godišnjaka iz pgz_sport.dokumenti.
|
||||
// Klik na karticu → otvara PDF u novom tabu preko /api/v2/dokumenti/godisnjak/{godina}.
|
||||
SECTIONS['pgz:dokumenti'] = async () => {
|
||||
// Cache godisnjaci na _state da ne dohvaćamo svaki put
|
||||
if(!_state._godisnjaci){
|
||||
try {
|
||||
const r = await fetch('/sport/api/v2/dokumenti/godisnjaci/list');
|
||||
const j = await r.json();
|
||||
_state._godisnjaci = (j && j.godisnjaci) || [];
|
||||
} catch(e) { _state._godisnjaci = []; }
|
||||
}
|
||||
const docs = _state._godisnjaci.slice().sort((a,b)=> (b.godina||9999) - (a.godina||9999));
|
||||
const fmtMB = b => b ? (b/1024/1024).toFixed(1)+' MB' : '—';
|
||||
const cards = docs.map(d => {
|
||||
const yr = d.godina || (d.izdano_datum ? String(d.izdano_datum).slice(0,4) : '—');
|
||||
const url = d.godina ? `/sport/api/v2/dokumenti/godisnjak/${d.godina}` : `/sport/api/v2/dokumenti/${d.id}/pdf`;
|
||||
return `
|
||||
<div class="card" style="cursor:pointer;transition:transform 0.15s, border-color 0.15s"
|
||||
onmouseover="this.style.borderColor='var(--gold,#F4C430)';this.style.transform='translateY(-2px)'"
|
||||
onmouseout="this.style.borderColor='';this.style.transform=''"
|
||||
onclick="window.open('${url}','_blank','noopener')">
|
||||
<div style="font-size:1.9rem;font-weight:700;color:var(--gold,#F4C430);line-height:1;margin-bottom:6px;letter-spacing:-1px">${esc(yr)}</div>
|
||||
<div style="font-weight:600;font-size:0.92rem;margin-bottom:6px">${esc(d.title || '(bez naslova)')}</div>
|
||||
<div style="color:var(--t2);font-size:11px;margin-bottom:4px">🏛️ ${esc(d.organizacija || '—')}</div>
|
||||
<div style="color:var(--t4);font-size:11px">📄 ${fmtMB(d.sadrzaj_size)}</div>
|
||||
</div>`;
|
||||
}).join('');
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-h">
|
||||
<div class="card-t">📚 Dokumenti — Godišnjaci ZSP PGŽ</div>
|
||||
<div class="card-actions">
|
||||
<a href="/sport/dokumenti" class="btn primary" style="text-decoration:none">📖 Otvori knjižnicu</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style="color:var(--t2);font-size:12px;margin-bottom:14px">
|
||||
${docs.length} godišnjaka u bazi · klik na karticu otvara PDF u novom tabu
|
||||
</div>
|
||||
${docs.length === 0
|
||||
? '<div class="empty">Nema godišnjaka u bazi.</div>'
|
||||
: `<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:12px">${cards}</div>`}
|
||||
</div>`;
|
||||
};
|
||||
|
||||
// =======================================================================
|
||||
// SAVEZ ADMIN — Dashboard + sub-pages
|
||||
// =======================================================================
|
||||
@@ -2095,12 +2155,31 @@ function navItemClick(item){
|
||||
if(item && item.id) navTo(item.id);
|
||||
}
|
||||
|
||||
// PGŽ priority filter helpers (CRISIS V4)
|
||||
// PGŽ priority filter helpers (CRISIS V4 / SUB6)
|
||||
window._pgz_filter_priority = window._pgz_filter_priority || false;
|
||||
window.togglePGZFilter = function(){
|
||||
window._pgz_filter_priority = !window._pgz_filter_priority;
|
||||
if(typeof loadSection === 'function') loadSection();
|
||||
};
|
||||
window.pgzBadgePrefix = function(it){
|
||||
const fin = !!(it && (it.financiran || it.klub_financiran || it.pgz_sufinanciran));
|
||||
const god = !!(it && (it.godisnjak || it.klub_godisnjak || (it.godisnjak_godine && (it.godisnjak_godine.length||0)>0)));
|
||||
const pgzs = !!(it && it.pgz_relevant);
|
||||
const pri = !!(it && it.priority) || fin || god || pgzs;
|
||||
if(!pri) return '';
|
||||
let s = '⭐';
|
||||
if(fin || pgzs) s += '💰';
|
||||
if(god) s += '📖';
|
||||
return s + ' ';
|
||||
};
|
||||
window.renderPGZToggleBtn = function(){
|
||||
const on = !!window._pgz_filter_priority;
|
||||
return '<button class="btn '+(on?'primary':'')+'" '
|
||||
+ 'style="margin:6px 8px 10px 0" '
|
||||
+ 'title="Prikaži samo PGŽ-financirane / u godišnjaku" '
|
||||
+ 'onclick="togglePGZFilter()">'
|
||||
+ (on ? '⭐ PGŽ filter ON' : '☆ PGŽ filter OFF') + '</button>';
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user