15 KiB
PGŽ SPORT PLATFORM — CLAUDE CODE HANDOFF
Datum: 2026-05-05 | Autor: Damir Radulić | dradulic@outlook.com
URGENTNO: Prezentacija županu sutra ujutro
SITUACIJA
Imamo sve podatke u bazi. Trebamo jedan funkcionalan HTML portal koji ih prikazuje.
Trenutni sport2.html je kaotičan - previše iteracija, bugovi se nakupljaju.
Zadatak: Napiši NOVI sport2.html od nule. Čist, brz, funkcionalan.
INFRASTRUKTURA
GPU Server: 144.76.68.5
SSH: ssh -p 5852 root@144.76.68.5 (pwd: 5852Dan1TR5852)
Bridge API: POST https://api.rinet.one/bridge/exec
Header: X-API-KEY: rinet-yS4ZnKlwUqsjk
Body: {"cmd": "bash command here"}
PostgreSQL: 10.10.0.2:6432
DB: rinet_v3, User: rinet, Pass: R1net2026!SecureDB#v7
Schema: pgz_sport.*
PGŽ Sport API: https://sport.rinet.one/sport/api/
Frontend fajl: /opt/pgz-sport/static/sport2.html
Service: systemctl restart pgz-sport
BAZA — ŠTO IMAMO (provjereni podaci)
-- Tablice i broj zapisa:
pgz_sport.savezi 246 -- savezi u PGŽ
pgz_sport.klubovi 2244 -- klubovi (aktivan=TRUE filter)
pgz_sport.clanovi 3243 -- sportaši
pgz_sport.clan_sezona 689 -- sezonske statistike (pretežno nogomet)
pgz_sport.utakmice_log 9267 -- log utakmica s club logovima
pgz_sport.clan_godisnjak 2398 -- godišnjaci 2006-2024
pgz_sport.clan_nagrada 2028 -- nagrade
pgz_sport.sportski_objekti 106 -- svi geocodirani (lat/lng popunjeni)
pgz_sport.manifestacije 113
pgz_sport.sufinanciranje_sport 110 -- financije 2024-2026
pgz_sport.dokumenti 5692 -- PDF dokumenti
pgz_sport.proracun 11 -- proračun po godinama
-- Sportaš s najboljim podacima (test case):
-- ID: 449, Josip Zec, NK OŠK Omišalj, 15 sezona, 257 nastupa, 182 gola
-- Ima sliku: https://hns.family/files/images_comet/70/d/...
-- Savezi imaju email (scraped iz sport-pgz.hr):
-- Atletski savez PGŽ: atletskisavezpgz@gmail.com
-- Bočarski savez PGŽ: info@bocarski-savez-pgz.hr
-- itd.
API ENDPOINTI (svi rade, testirani)
BASE = https://sport.rinet.one/sport/api
GET /dashboard # KPIs, top savezi, proračun
GET /savezi?limit=250 # Lista saveza
GET /savezi/{id}/full # Detalji saveza + klubovi + clanovi + treneri
GET /klubovi?limit=100&sport=X&grad=Y&nositelj=true
GET /klubovi/{id} # Detalji kluba + clanovi + potpore
GET /clanovi-full?limit=80&q=X&hoo=2&reprezentativac=true
GET /sportas/{id}/profil # KLJUČNI endpoint - vraća sve stats
# Ima: clan_sezona, utakmice, nagrade, godisnjaci, stats{}
GET /sportski-objekti # 106 objekata, svi imaju lat/lng float
GET /manifestacije-full # 113 manifestacija
GET /sufinanciranje?limit=500&godina=2026&sport=X
GET /v2/analytics/proracun-sport?godina=2026 # raspodjela po sportovima
GET /v2/potpore/by-year?godina=2026 # primatelji za godinu
GET /gradovi # normalizirani popis gradova
GET /dokumenti?tip=X&q=Y # PDF dokumenti
ŠTO PORTAL MORA RADITI
1. NAVIGACIJA (sidebar ili top tabs)
📊 Dashboard | 🏅 Savezi | ⬡ Klubovi | 👤 Sportaši |
€ Financije | 📍 Objekti | 📅 Manifestacije | ⚠ Forenzika
2. DASHBOARD (prva stranica)
- KPI kartice: Saveza (246), Klubova (2244), Sportaša (3243), Proračun 2026 (€8.17M)
- Grafikon proračuna po godinama (2016-2026)
- Top 10 saveza po broju registriranih
- Klik na godinu → otvori panel s popisom primatelja novca
3. SAVEZI (grid kartice, klikabilne)
- Filter: pretraži naziv, filter po sportu
- Card/Table toggle
- KLIK NA SAVEZ → panel desno koji prikazuje:
- Ime, sport, predsjednik, tajnik, email (link), web (link)
- 3 broja: Klubova / Sportaša / Trenera
- Tabovi: Klubovi (tablica, klikabilna) | Sportaši (kartice, klikabilne) | Treneri
4. KLUBOVI (grid kartice, klikabilne)
- Filteri: pretraži, sport, grad, razina, nositelj kvalitete checkbox
- Card/Table toggle (OBA view puniti odmah, ne čekati klik)
- KLIK NA KLUB → panel koji prikazuje:
- Info: predsjednik, tajnik, OIB, sjedište, savez
- Score baza podataka (ima li oib/predsjednika/web itd)
- Tabovi: Sportaši | Potpore | Info
5. SPORTAŠI — HNS SEMAFOR STIL (najvažnije)
- Grid kartica s fotografijom (ako postoji), imenom, sportom, klubom
- Filter: pretraži ime, HOO kategorija, samo reprezentativci
- Card/Table toggle
- KLIK NA SPORTAŠA → panel koji prikazuje:
┌─────────────────────────────────────────────┐
│ [FOTO 90x110] Josip Zec │
│ nogomet · Igrač · NK OŠK Omišalj │
│ [Aktivan] [#18] │
│ 📅 1994-01-01 │
├─────────────────────────────────────────────┤
│ 257 182 0 75 39 15 │
│ NASTUPI GOLOVI ASIST. ŽUTI CRVENI SEZ. │
├─────────────────────────────────────────────┤
│ [Sezone (15)] [Utakmice (16)] [Bio] [God.] │
├─────────────────────────────────────────────┤
│ Sezone tab (default): │
│ SEZONA | NATJECANJE | NAS | GOL | ŽUT │
│ 2025/26 | Treća NL Zapad | 16 | 12 | 4 │
│ 2024/25 | 4. NL NS Rijeka| 21 | 11 | 10 │
│ ... │
└─────────────────────────────────────────────┘
API za profil: GET /sport/api/sportas/449/profil Vraća: { ime, prezime, slika_url, sport, pozicija, klub_naziv_full, stats: { ukupno_nastupa, ukupno_pogodaka, ukupno_asistencija, ukupno_zutih, ukupno_crvenih, sezone_aktivne }, clan_sezona: [{sezona, natjecanje, nastupi, pogoci, asistencije, zuti_kartoni, crveni_kartoni, natjecanje_url}], utakmice: [{datum, klub_dom, klub_gost, klub_dom_logo, klub_gost_logo, rezultat, pogodaka, zuti_kartoni, minute}], godisnjak_godine: [2006, 2007, ...] }
6. FINANCIJE
- Filteri: godina (2024/2025/2026), sport, naziv korisnika
- KPI: Ukupno / Broj primatelja / Sportova / Razina
- Pie/Bar chart po sportu
- Tablica svih primatelja s linkom na izvorni PDF
7. SPORTSKI OBJEKTI
- Card/Table toggle
- Google Maps link: ako ima lat/lng → https://maps.google.com/maps?q={lat},{lng}&z=17
- Embed iframe u kartici ako ima koordinate
- Filter: grad, tip (stadion/bazen/dvorana...)
8. MANIFESTACIJE
- Card/Table toggle
- Klik na karticu → otvori source_url ako postoji, inače popup s podacima
- Filter: naziv, razina
9. FORENZIKA
- Lista nalaza s severity (CRITICAL=crvena, HIGH=žuta)
- Velimir Liverić profil (ručno hardkodirani jer je politički osjetljiv)
10. MREŽA (Network Graf)
- D3 force graph
- 3 search polja: Osoba | Klub/Savez | Tvrtka
- Tip filter: Svi / Osobe / Klubovi / Tvrtke / Forenzički
- Klik na čvor → otvori panel s informacijama
- Reset gumb
PANEL (desno, slide-in)
<div id="panel" style="position:fixed;top:0;right:-580px;width:560px;height:100vh;
background:#0d1021;border-left:1px solid #1e2a50;z-index:200;
transition:right .25s;display:flex;flex-direction:column">
<div id="panel-hdr" style="padding:12px 14px;border-bottom:1px solid #1e2a50;
display:flex;align-items:center;justify-content:space-between;flex-shrink:0">
<div id="panel-hdr-t" style="font-size:13px;font-weight:700;color:#fff">Detalji</div>
<div onclick="closePanel()" style="cursor:pointer;font-size:20px;color:#4e5a7a">×</div>
</div>
<div id="panel-body" style="flex:1;overflow-y:auto;padding:14px"></div>
</div>
<div id="panel-overlay" onclick="closePanel()" style="display:none;position:fixed;
inset:0;background:rgba(0,0,0,.4);z-index:199"></div>
DIZAJN (obavezni CSS vars)
:root {
--pgz-blue: #003087;
--pgz-blue2: #004CC4;
--pgz-gold: #F4C430;
--bg0: #08090e;
--bg1: #0d1021;
--bg2: #111628;
--bg3: #161d35;
--bg4: #1c2542;
--rim: #1e2a50;
--rim2: #283560;
--t0: #fff;
--t1: #e2e6f0;
--t2: #8a95b4;
--t4: #4e5a7a;
--green: #00e88f;
--red: #ff2d55;
--amber: #f59e0b;
--cyan: #00c8e8;
--font: 'Inter', sans-serif;
--mono: 'JetBrains Mono', monospace;
}
Fonts: Inter + JetBrains Mono (Google Fonts) Libraries: D3.js v7, Chart.js v4 (CDN cdnjs.cloudflare.com)
KRITIČNA PRAVILA
- Nikada nemoj patchati stari fajl — piši novi od nule
- Svaka JS funkcija mora biti definirana — ne pozivaj što nisi napisao
- Template literals — unutar
${}NE koristiš\', koristiš' - Python triple quotes — ne koristi
'''u Python stringovima koji generiraju JS - Uvijek provjeri Node.js syntax prije deploya:
node --check file.js - Deploy metodologija (fajl je ~100-120KB):
import base64 # Na serveru: python3 -c "import base64; open('/opt/...','wb').write(base64.b64decode('{b64}'))" # Za veće fajlove: split u chunkove od 30KB, append
DEPLOY SCRIPT
import subprocess, json, base64
def deploy_file(local_path, remote_path):
"""Deploy file to server via Bridge API"""
BRIDGE = "https://api.rinet.one/bridge/exec"
KEY = "rinet-yS4ZnKlwUqsjk"
content = open(local_path, 'rb').read()
CHUNK = 30000
chunks = [content[i:i+CHUNK] for i in range(0, len(content), CHUNK)]
for i, chunk in enumerate(chunks):
b64 = base64.b64encode(chunk).decode()
mode = 'wb' if i == 0 else 'ab'
cmd = f"python3 -c \"import base64; open('{remote_path}','{mode}').write(base64.b64decode('{b64}'))\""
r = subprocess.run(['curl','-sX','POST',BRIDGE,
'-H',f'X-API-KEY: {KEY}',
'-H','Content-Type: application/json',
'-d',json.dumps({"cmd":cmd})],
capture_output=True, text=True, timeout=30)
print(f"Chunk {i+1}/{len(chunks)}: {'OK' if r.returncode==0 else 'ERR'}")
# Restart service
r2 = subprocess.run(['curl','-sX','POST',BRIDGE,
'-H',f'X-API-KEY: {KEY}',
'-H','Content-Type: application/json',
'-d',json.dumps({"cmd":"systemctl restart pgz-sport && sleep 2 && curl -s http://localhost:8095/health"})],
capture_output=True, text=True)
print("Service:", json.loads(r2.stdout).get('stdout','')[-60:])
deploy_file('/home/claude/sport2_new.html', '/opt/pgz-sport/static/sport2.html')
AGENT TASK PODJELA (za tmux swarm)
Agent W1 — HTML Skeleton + CSS + Navigation
Napiši potpuni HTML skeleton s:
- CSS varijablama i komponentama
- Sidebar navigacija (11 sekcija)
- Panel HTML (desno slide-in)
- Sve <div id="pg-X"> sekcije prazne
- initHeroCanvas() particle funkcija
Spremi: /home/claude/sport2_skeleton.html
Agent W2 — Dashboard + Proračun
Implementiraj:
- initDash() → GET /sport/api/dashboard → KPI kartice
- loadProracun() → chart + klik na godinu
- loadProracunDrill(god) → GET /sport/api/v2/potpore/by-year?godina=X
Dodaj u skeleton
Agent W3 — Savezi + openSavez panel
Implementiraj:
- loadSavezi() → GET /sport/api/savezi?limit=250
- renderSaveziGrid(data) s card i table mode
- openSavez(s) → GET /sport/api/savezi/{id}/full → panel s tabovima
Sve klikabilno!
Agent W4 — Klubovi + openKlub panel
Implementiraj:
- loadKlubovi() s filterima (sport, grad, razina, nositelj)
- buildKlubCard(k) + buildKlubTable(rows)
- Uvijek puni i kartice i tablicu (ne samo jedan view)
- openKlub(id) → GET /sport/api/klubovi/{id} → panel
Agent W5 — Sportaši + openSportas panel (HNS Semafor)
Implementiraj:
- loadSportasi() s filterima
- buildPlayerCard(c) s fotografijom
- openSportas(id) → GET /sport/api/sportas/{id}/profil
- Panel: foto + stats bar (nastupi/goli/asist/žuti/crveni/sezone)
- Tabovi: Sezone (tablica) | Utakmice (s logovima klubova) | Bio | Godišnjaci
Agent W6 — Financije + Sufinanciranje
Implementiraj:
- loadFinancije() → GET /sport/api/sufinanciranje?limit=500
- Filteri: godina, sport, naziv
- Chart.js pie/bar chart po sportu
- Tablica svih primatelja s linkovima na PDF
Agent W7 — Sportski Objekti + Google Maps
Implementiraj:
- loadObjekti() → GET /sport/api/sportski-objekti
- buildObjektCard(o): ako ima lat/lng → Maps URL = https://maps.google.com/maps?q={lat},{lng}&z=17
- Iframe embed u kartici za objekte s koordinatama
- Card/Table toggle
Agent W8 — Manifestacije + Mreža
Implementiraj:
- loadManifest() → klikabilne kartice → source_url ili popup
- loadNetwork(q) → GET /sport/api/network/pgz?q=X
- D3 force graph s search inputima
- Klik na čvor → openPanel s podacima
Agent W9 — Deploy + QA
- Provjeri Node.js syntax: node --check /tmp/sport2_new.js
- Deploy skriptu gore
- Test svaki endpoint
- Provjeri da nema undefined funkcija
- Verifikacija: curl -s https://sport.rinet.one/ | grep "function openSavez"
Agent W0 (Orchestrator) — Merge + Deploy
- Čekaj sve agente
- Merge svih HTML/JS dijelova u jednu datoteku
- Syntax check
- Deploy
- Test s 10 različitih klikova
VALIDACIJA (svaki agent mora proći ovo)
# 1. Syntax check
python3 -c "
import re
html = open('/home/claude/sport2_new.html').read()
m = re.search(r'<script>(.*)</script>', html, re.DOTALL)
open('/tmp/check.js','w').write(m.group(1))
"
node --check /tmp/check.js
# 2. Nije undefined
curl -s https://sport.rinet.one/ | grep "function openSavez"
curl -s https://sport.rinet.one/ | grep "function searchNetwork"
curl -s https://sport.rinet.one/ | grep "function openSportas"
# 3. API radi
curl -s https://sport.rinet.one/sport/api/sportas/449/profil | python3 -c "
import json,sys; d=json.load(sys.stdin)
assert d.get('stats',{}).get('ukupno_nastupa') == 257, 'Stats broken'
print('API OK - Josip Zec:', d['stats']['ukupno_nastupa'], 'nastupa')
"
CILJ DO JUTRA 08:00
Portal na https://sport.rinet.one/ koji:
- Otvori se i prikaže dashboard s KPI brojkama
- Klik na Savez → panel s klubovima
- Klik na Klub → panel sa sportašima
- Klik na Sportaš → profil s karijernim statistikama i fotografijom
- Financije → kto dobiva javni novac i koliko
- Objekti → na karti
- Nema JS grešaka u konzoli
Josip Zec (ID 449) je test case. Mora prikazivati 257 nastupa, 182 gola, 15 sezona.
NAPOMENE
- Damir je umoran. Ne pitaj za permisije. Radi autonomno.
- Ako nešto ne radi, debugiraj sam
- Ne patchaj stari fajl. Novi od nule.
- Koristiš Bridge API za sve server operacije
- CSS je dark theme, PGŽ boje (blue #003087, gold #F4C430)
- Sve interakcije (klikovi, tabovi, filtri) moraju raditi