Kako je složen LumenSpark
Vodič kroz arhitekturu aplikacije pisan tako da ga može pratiti i developer koji tek počinje. Svaka tema ide redom: što je to → kako radi baš ovdje → primjer iz koda.
Uvod - što je LumenSpark
LumenSpark je interni alat za vođenje projekata u agenciji (LumenHope PM). Zamjenjuje Jira/ClickUp i objedinjuje: projekte, zadatke (Kanban ploča), evidenciju radnog vremena, odsustva, troškove, izvještaje i analitiku - te povlači podatke iz Clockifyja i Jire.
Koriste ga četiri tipa korisnika: superadmin, admin, voditelj projekta (PM) i zaposlenik. Svaki vidi i smije različite stvari (vidi poglavlje o ulogama).
Najnoviji sloj je AI pomoć (Anthropic Claude): od ⌘K trake za pitanja do izvršnog sažetka. Pravilo je strogo - brojke uvijek računa kod, AI ih samo opisuje ljudskim jezikom (vidi poglavlje o AI-u).
Tehnologije (stack)
Namjerno je biran jednostavan, klasičan stack bez velikih frameworka - lakše ga je hostati na običnom cPanel serveru i lakše ga je razumjeti.
| Sloj | Tehnologija | Čemu služi |
|---|---|---|
| Backend | PHP 8.2 | Logika na serveru, obrada zahtjeva |
| Baza | MySQL + PDO | Pohrana podataka (sigurni upiti) |
| Frontend | Čisti JavaScript (Vanilla) | Sučelje, bez Reacta/Vuea |
| Grafovi | Chart.js | Dijagrami u analitici |
| Drag & drop | SortableJS | Povlačenje kartica na Kanbanu |
| pdfmake (+ html2canvas) | Izvoz izvještaja u PDF | |
| AI | Anthropic Claude API | Sažetci, narativi i ⌘K asistent |
| Hosting | cPanel (lumenspark.eu) | Klasičan shared server |
Velika slika arhitekture
LumenSpark ima tri jasna sloja: preglednik (ono što korisnik vidi), server (PHP) i baza. Kad korisnik nešto klikne, frontend pošalje zahtjev na jedan ulaz na serveru - api/index.php - koji prepozna što se traži, proslijedi pravom kontroleru, a ovaj razgovara s bazom i vrati odgovor.
- ① Zahtjev - frontend šalje
fetch POSTs poljemaction(npr.get_projects) i sigurnosnimCSRFtokenom. - ② Upit - router u mapi ruta nađe kontroler; on provjeri prijavu i izvrši pripremljeni SQL upit (PDO).
- ③ Odgovor - baza vrati podatke, kontroler ih pošalje kao JSON, frontend osvježi ekran (
render()).
api/index.php. Tako se na jednom mjestu provjeri prijava i sigurnost te odluči kamo zahtjev ide. To je router.Struktura mapa + pregled koda
Cijela aplikacija živi u mapi app/. Prvo gdje je što, a zatim klikabilni pregled svih kontrolera, JS modula i CSS datoteka.
- app/
index.html- jedina HTML stranica (SPA)config.php- pristup bazi (ne dira se)- api/ - backend (PHP): router + 37 kontrolera
- js/ - frontend: 79 modula
- css/ - 73 CSS (71 učitanih)
- language/ - prijevodi hr / en / de
- install/ - instalacija + migracije baze
- tests/ - automatski testovi
- assets/ - slike, logo, fontovi
- uploads/ - avatari, logotipi (učitano)
ime.js i ime.css s istim prefiksom (npr. kanban-). Niže klikni bilo koju datoteku da vidiš njezine metode/funkcije, varijable i objašnjenje.Klikabilni pregled koda
Odaberi skupinu, klikni stavku - desno se otvori popis metoda/funkcija, varijabli i kratko objašnjenje.
Backend - router, kontroleri, sigurnost
Router (api/index.php)
Frontend uvijek šalje POST zahtjev s poljem action (npr. "get_projects"). Router ima mapu ruta koja svaku akciju spaja s kontrolerom i metodom (ukupno 180 ruta):
$routes = [
'get_projects' => [Projects::class, 'getAll'],
'save_project' => [Projects::class, 'save'],
'move_task' => [Kanban::class, 'moveTask'],
// ... još 177 ruta
];
Router također vraća uvijek JSON i ima globalni „hvatač grešaka" - ako nešto pukne, korisnik dobije uredan JSON, a ne ružnu PHP grešku.
BaseController - zajednička osnova
Svaki kontroler nasljeđuje BaseController, koji nudi alate koje svi koriste:
respond($data)- vrati uspješan JSON odgovorrespondError(code, msg, status)- vrati greškuvalidate($rules)- provjeri ulazna polja (required|min|max|email|numeric|date)requireAuth()- zahtijeva prijavu (inače 401)requireAdmin()- zahtijeva admin/superadmin ulogu (inače 403)
public function getAll(): void {
$this->requireAuth(); // 1. mora biti prijavljen
$rows = $this->pdo
->query("SELECT * FROM ls_projects ORDER BY name ASC")
->fetchAll(); // 2. dohvati iz baze
$this->respond($rows); // 3. vrati JSON
}
Sigurnost
| Mehanizam | Kako radi |
|---|---|
| Sesija | Nakon prijave server pamti korisnika u $_SESSION['ls_user'] |
| CSRF token (default-deny) | Svaki zahtjev nosi tajni token (X-CSRF-Token). Model je obrnut: token se traži za sve akcije osim malog popisa izuzetaka - tako je svaka nova ruta sigurna po defaultu |
| Javne vs zaštićene akcije | Samo login, logout, check_auth i nekoliko test-akcija su javne; sve ostalo traži prijavu |
| Uloge | requireAdmin() + provjera vlasništva (zaposlenik dira samo svoje) |
| Enkripcija | Tokeni konektora (Clockify/Jira/Slack/Anthropic) šifrirani (AES-256-CBC) u bazi |
| Lozinke | bcrypt hash + ograničavanje pokušaja prijave (rate-limit/lockout) i obnova sesije |
| CORS | Server prima zahtjeve samo s domene lumenspark.eu |
Baza podataka
Sve tablice imaju prefiks ls_. Glavne:
| Tablica | Čemu služi |
|---|---|
ls_users | Korisnički računi i lozinke za prijavu |
ls_members | Članovi tima (i njihova satnica/trošak) |
ls_projects | Projekti (status, budžet, klijent, izvor) |
ls_tasks | Zadaci / Kanban kartice (kolona, sprint, epic) |
ls_time_entries | Evidencija radnog vremena |
ls_absences / ls_absence_types | Odsustva (godišnji, bolovanje…) i njihovi tipovi |
ls_expenses / ls_expense_categories | Troškovi i kategorije troškova |
ls_milestones | Prekretnice na projektima |
ls_partners | Poslovni partneri / klijenti |
ls_settings / ls_company_settings | Postavke aplikacije i tvrtke |
ls_active_timers | Timeri koji trenutno teku |
id u ls_projects i ls_members je VARCHAR (jer dolazi kao tekstualni ID iz Clockifyja/Jire), a ne broj. ls_tasks.id jest broj. Lako se zaboravi.ls_ tablica (ukupno ~40) kroz SQL migracije - npr. ls_roles + ls_role_modules (matrica prava), ls_board_columns + ls_sprints (Kanban), ls_ai_usage (potrošnja AI-a), ls_audit_log (revizijski trag), ls_billing_phases (faze naplate), ls_allocations (alokacije).Kako se mijenja shema
Ne dira se baza „ručno napamet". Promjene idu kroz datoteke install/migrate_*.php ili kroz SQL koji se zalijepi u phpMyAdmin (server nema pristup information_schema, pa se koriste samo SHOW + izmjene po tablici).
Frontend - kako radi sučelje
Frontend je SPA (Single Page Application) - postoji samo index.html, a JavaScript mijenja sadržaj bez ponovnog učitavanja stranice. index.html učitava ~89 skripti određenim redom (redoslijed je bitan).
Memorija - state.js (objekt S)
Sve što aplikacija „pamti" dok radi živi u jednom globalnom objektu S: koji je ekran otvoren (S.activeTab), tko je prijavljen (S.currentUser, S.currentUserRole), učitani projekti i članovi (S.projectsDB, S.membersDB) itd.
Pokretanje - boot.js
Prikazuje preloader, vodi prijavu (doLogin), postavlja korisnika (setCurrentUser) i slaže izbornik prema ulozi (_applyRoleSidebar - skriva/pokazuje stavke).
Crtanje ekrana - render.js
Funkcija renderTab() gleda S.activeTab i poziva pravu funkciju za taj ekran:
function renderTab(){
if (S.activeTab === 'dashboard') { renderDashboard(); return; }
if (S.activeTab === 'kanban') { renderKanban(); return; }
if (S.activeTab === 'projects_db'){ renderProjectsDB(); return; }
// … itd.
}
Klikovi - events.js
Umjesto da svaki gumb ima svoj onclick, koristi se delegacija: jedan slušač na cijelom dokumentu hvata klik i gleda atribut data-action.
document.addEventListener('click', function(e){
var t = e.target.closest('[data-action]');
if (!t) return;
var act = t.getAttribute('data-action'); // npr. "edit-proj"
// … pozovi odgovarajuću funkciju
});
Prijevodi - language/
Tekstovi se ne pišu „tvrdo" nego kroz funkciju t('kljuc'), koja vrati prijevod za trenutni jezik (hr/en/de). Ako prijevod ne postoji, vrati se sam ključ.
Dizajn sustav i CSS
Da sve izgleda jednako, postoji jedan izvor istine za stil u app.css: tokeni (boje, razmaci, zaobljenja) i kanonske komponente koje se koriste posvuda.
Tokeni (varijable boja)
Boje se nikad ne pišu kao „goli" hex, nego preko varijabli - promjena na jednom mjestu mijenja cijelu aplikaciju.
--brand:#1c4532; --green:#0a7c4e; --blue:#0066cc;
--bg:#f4f4f0; --bg2:#ffffff; --border:#ddddd8;
Kanonske komponente
| Komponenta | Za što |
|---|---|
.th / .tr / .col-h | Tablice (sve tablice izgledaju isto) |
.s-btn (+.primary/.sec) | Gumbi u zaglavljima i modalima |
.icon-btn (+.danger) | Mali gumbi u redovima tablice |
.badge (+.b-green…) | Statusne „pilule" (Aktivan, Završen…) |
.modal / .empty-s / .tt | Skočni prozori, prazna stanja, tooltipovi |
Modul table-enhance automatski daje svim tablicama sortiranje (klik na zaglavlje) i nježni zeleni rollover reda pod mišem.
Kako je CSS posložen (slojevi)
CSS se gradi u slojevima - od temelja prema gore. Svaki viši sloj smije koristiti i nadograditi ono ispod sebe, ali temelj (app.css) ne ovisi ni o čemu.
Redoslijed učitavanja - „koji se vuku"
index.html učitava 71 CSS datoteke točnim redom. Najvažnije pravilo: app.css ide PRVI, jer definira tokene i komponente o kojima ovise svi ostali.
| Sloj | Datoteke (redom) | Uloga |
|---|---|---|
| 1 · Temelj | app.css | Tokeni + sve kanonske komponente (svi ovise o njemu) |
| 2 · Ponašanje | table-enhance.css | Sort + row-hover za sve tablice |
| 3 · Okvir | sidebar-brand · sidebar · sidebar-nav · subpage · slideover · render · boot · load · preloader | Izbornik, podstranice, paneli, učitavanje |
| 4 · Moduli | reporting · settings · projects · kanban · gantt · analytics · partners … (~40) | Stil pojedine značajke - prefiks ime- |
| 5 · KPI trake | dashboard-kpi · employee-kpi · expenses-kpi · vacation-kpi · kpi-strip … | Kartice s pokazateljima |
app.css se ne dira napamet - promijeniš li token tamo, mijenja se cijela aplikacija. Stil pojedine značajke ide u njezin ime.css (s prefiksom). Cijeli klikabilni popis svih CSS datoteka je u poglavlju 04.landing.css se učita na login ekranu, a app-legacy.css je stari (postupno se uklanja).Uloge i prava
Vidljivost u izborniku i dozvole na serveru ovise o ulozi:
| Uloga | Što vidi / smije |
|---|---|
| superadmin | Sve, uključujući tehničke postavke i sistemske alate |
| admin | Svi projekti, financije, tim, postavke, Kanban |
| PM | Svoji projekti, tim & analitika, Kanban - bez punih financija |
| zaposlenik | Dashboard, svoji projekti, moja statistika, radno vrijeme, Kanban (vuče samo svoje kartice) |
Matrica prava (RBAC) - can()
Prava se ne provjeravaju usporedbom imena uloge razbacanom po kodu, nego kroz jednu matricu: tablice ls_roles (uloge) × ls_role_modules (koji modul i na kojoj razini smije uloga). Postoji jedan središnji „rješavač":
- Backend:
Permissions.phpnapuni prava u sesiju jednom pri loginu; svaka akcija pitacan(modul, razina). - Frontend:
perms.jsnudilsCan()/canModule()- sučelje sakrije ono što korisnik ne smije.
Na frontendu izbornik slaže _applyRoleSidebar() (liste empHidden/empVisible u boot.js). Sigurnosno pravilo ostaje: prava zaštita je na serveru.
if role === 'admin') na stotinama mjesta - teško za održavanje i lako za grešku. Sada je sve na jednom mjestu: dodaš redak u matricu i prava odmah vrijede.Integracije - Clockify i Jira
Aplikacija povlači podatke iz vanjskih alata. Postavljaju se u Postavke → Konektori (API ključ se šifrira prije spremanja).
- Clockify - sinkronizira projekte, članove i evidenciju vremena.
- Jira - uvozi zadatke (issues) u
ls_taskskao Kanban kartice (REST v3 proxy). - Slack - šalje obavijesti o događajima (npr. novi zadatak, odobreno odsustvo) na kanal.
- Anthropic Claude - AI pozivi za sažetke i narative (vidi poglavlje o AI-u).
Uvoz je jednosmjeran i ponovljiv (pull/upsert): povuče i ažurira, ali ne piše natrag u Clockify/Jiru. Pokreće se ručno i siguran je za ponavljanje (idempotentan).
AI značajke
LumenSpark ima ugrađen AI sloj koji koristi Anthropic Claude. On ne zamjenjuje aplikaciju - on je pomoćnik koji čita gotove podatke i objašnjava ih ljudskim jezikom.
Šest živih AI značajki
| Značajka | Akcija (ruta) | Što radi |
|---|---|---|
| ⌘K Spark traka | ai_ask | Brza traka (tipka ⌘K / Ctrl+K) za pitanja o tvrtki; pamti tijek razgovora (multi-turn) |
| AI sažetak projekta | ai_project_summary | Kratak narativ o stanju projekta |
| AI prijedlog tima | ai_team_suggest | Prijedlog tko bi mogao raditi na projektu |
| AI prijedlog backloga | ai_backlog_suggest | Prijedlog zadataka za Kanban backlog |
| AI tumačenje ekrana | ai_narrate | Gumb „AI tumačenje" na Novčanom tijeku i Utilizaciji |
| Izvršni narativ | exec_narrative | Sažetak / Financije / Rizici / Preporuke za upravu (vidi Exec360) |
Kako radi (jednostavno)
api/Ai.phpje tanki shim - jedna ulazna točka koja zove Anthropic API i vraća tekst.- API ključ je šifriran (AES-256-CBC) u
ls_settings; postavlja se u Konektorima. - Svaki poziv se zabilježi u
ls_ai_usage(model, tokeni, procijenjeni trošak) - da se vidi potrošnja. api/AiAssist.phpiapi/AiNarrate.phpprvo sastave snapshot (gotove brojke iz PHP-a), pa to pošalju AI-ju da napiše tekst.
requireAuth) i CSRF token, jednako kao i ostale osjetljive rute.Kanban i vođenje projekata
Kanban je vizualna ploča sa zadacima koji putuju kroz kolone (npr. Za napraviti → U tijeku → Gotovo). Cilj mu je zamijeniti Jiru za svakodnevni rad.
| Dio | Tablica / tehnologija | Objašnjenje |
|---|---|---|
| Kartice (zadaci) | ls_tasks | Tip (epic/story/task/bug), prioritet, story points, izvršitelj, kolona |
| Kolone | ls_board_columns | Po projektu; svaki projekt ima svoju ploču (1 projekt : 1 ploča) |
| Sprintovi | ls_sprints | Samo za projekte tipa scrum; kanban projekti ih ignoriraju |
| Povlačenje kartica | SortableJS | Drag & drop kartice iz kolone u kolonu |
Polje board_type na projektu određuje radi li se o čistom Kanbanu ili Scrumu (sa sprintovima). Zadaci se mogu uvesti iz Jire (jednosmjerno, ponovljivo), a na svaki zadatak može se klokati vrijeme (timer veže sat uz karticu).
ls_tasks.Sigurnost - detaljno
Sigurnost je posložena u više slojeva. Junioru je najvažnije zapamtiti: provjera je uvijek na serveru, a podaci se nikad ne lijepe „goli" u SQL.
| Sloj | Kako štiti |
|---|---|
| Prijava | Lozinke su bcrypt hash; previše pogrešnih pokušaja privremeno zaključa; sesija se obnovi (regenerate) pri prijavi |
| CSRF (default-deny) | Token se traži za sve akcije osim malog popisa javnih - svaka nova ruta je sigurna po defaultu |
| SQL injection | Pripremljeni upiti (PDO) posvuda - vrijednosti idu odvojeno od SQL teksta |
| XSS | Sav tekst koji dolazi od korisnika prolazi kroz esc() / escJs() prije ispisa |
| Tajne | Tokeni konektora šifrirani (AES-256-CBC); nikad u čistom tekstu |
| Server (.htaccess) | Sigurnosni headeri + CSP; blokiran pristup config.php, logovima i instalaciji |
| Prava (RBAC) | Središnja matrica can(modul, razina) - vidi poglavlje o ulogama |
Izvršni izvještaj (Exec360)
Exec360 je jedan ekran za upravu - „cockpit" koji na jednom mjestu pokaže novac i smjer tvrtke: KPI kartice (prihod, trošak, dobit, marža), grafove i kratak AI sažetak.
exec_snapshot- jedan endpoint koji agregira sve ključne brojke (PHP ih izračuna).exec_narrative- AI iz tog snapshota napiše Sažetak → Financije → Rizici → Preporuke.- Izvještaj se može izvesti u PDF (pdfmake, dizajn iz
reporting-pdf.css).
Chrome ekstenzija (Spark)
Uz aplikaciju postoji i mala Chrome ekstenzija (MV3, verzija 1.1.3) za brzo klokanje vremena. Zaposlenik iz alatne trake preglednika odabere projekt i zadatak te pokrene/zaustavi mjerenje - bez otvaranja cijele aplikacije.
| Svojstvo | Detalj |
|---|---|
| Što mijenja na serveru | Ništa - koristi postojeće rute (login, check_auth, timer_start/stop/status) |
| Gdje živi timer | Na serveru (ls_active_timers) - broji i kad je preglednik ugašen |
| Dozvole | Minimalne: samo storage + alarms; pristup samo domeni lumenspark.eu |
check_auth) da se dobije svjež CSRF token, a tek onda javi status timera.Životni ciklus zahtjeva - korak po korak
Pratimo što se dogodi kad korisnik klikne npr. „Spremi projekt":
- Klik u pregledniku - gumb ima
data-action="save-project". events.jsuhvati klik i pozove pravu JS funkciju.- Funkcija pozove
apiCall('save_project', podaci)- to jefetch POSTna/api/sX-CSRF-Tokenu zaglavlju. api/index.phppročitaactioni u mapi ruta nađe[Projects, 'save'].- Kontroler napravi
requireAuth(), provjeri podatke i izvrši pripremljeni SQL upit. - Vrati JSON:
{ "ok": true, … }ili grešku{ "ok": false, "error": … }. - JS osvježi
S(memoriju) i ponovno nacrta ekran (renderTab()).
Testiranje
Postoje tri sloja provjere - cilj je da se promjena ne „provuče" i pokvari nešto drugo:
| Sloj | Što provjerava | Kako se pokreće |
|---|---|---|
| 1 · Browser asserti | ~2800 provjera ponašanja u tests.js | U aplikaciji (gumb „Debug") |
| 2 · Node guardovi | Provjere na razini koda (svaki tests/*.test.js) | node tests/ime.test.js |
| 3 · Lint pravila | Bez golog hexa, bez sirovih <table> u modulima… | node tests/lint-rules.js |
Deploy (objava na server)
Objava ide ručno preko cPanel File Managera (nema terminala ni lokalnog servera):
- Uploadaj promijenjene datoteke u odgovarajuće mape (
js/,css/,api/…). - Promijeni
?v=broj uz CSS/JS uindex.html- inače preglednik servira staru verziju iz keša. - Promjene baze: zalijepi SQL u phpMyAdmin.
- Otvori aplikaciju uz Ctrl + Shift + R (tvrdi refresh).
?v= bump → preglednik pokaže staru datoteku i čini se da „promjena ne radi". Uvijek povećaj broj.Rječnik pojmova
| Pojam | Objašnjenje |
|---|---|
| SPA | Single Page App - jedna HTML stranica, JS mijenja sadržaj |
| Router | Dio koji odlučuje koji kod obrađuje koji zahtjev |
| Endpoint / akcija | Imenovana operacija na serveru (npr. get_projects) |
| Kontroler | PHP klasa koja obrađuje skup povezanih akcija |
| Sesija | Server pamti prijavljenog korisnika između zahtjeva |
| CSRF token | Tajni ključ koji dokazuje da zahtjev dolazi iz naše aplikacije |
| PDO | PHP-ov siguran način razgovora s bazom |
| Prepared statement | Upit gdje su vrijednosti odvojene od SQL teksta (sigurnost) |
| Token (tokeni boja) | CSS varijabla, npr. var(--brand) |
| i18n | Višejezičnost (hr/en/de) |
| Slide-over | Panel koji „uđe" sa strane (detalj kartice) |
| SSOT | Single Source Of Truth - jedna službena verzija koda (zadnji ZIP) |
| RBAC | Role-Based Access Control - prava se dodjeljuju ulogama kroz matricu (uloge × moduli) |
| can() / lsCan() | Središnja provjera „smije li ova uloga ovo" (backend i frontend) |
| Grounding | AI koristi isključivo gotove, izračunate brojke - ne izmišlja |
| Snapshot | Paket gotovih brojki koji PHP složi i pošalje AI-ju da napiše tekst |
| Shim | Tanki posrednički sloj prema vanjskom servisu (npr. Ai.php prema Anthropicu) |
| MV3 | Manifest V3 - moderni format Chrome ekstenzija (pozadinski worker se gasi) |
| Default-deny | Sigurnosni model: sve je zabranjeno osim izričito dopuštenog (npr. CSRF izuzeci) |
| RAG | Status: zeleno / žuto / crveno |
Recept: kako dodati novu funkcionalnost
Kratki podsjetnik - koraci kad dodaješ npr. novi popis na ekranu:
- Backend: dodaj metodu u kontroler + upiši rutu u mapu
$routes(srequireAuth()i pripremljenim upitom). - Frontend: napravi
ime.js+ime.css(isti prefiks) i registriraj ih uindex.html. - Komponente: koristi kanonske (
.th/.tr,.s-btn,.badge…) i tokene - bez golog hexa. - Jezici: dodaj tekstove u sva tri (hr/en/de).
- Uloge: provjeri tko smije vidjeti/raditi (gate na serveru!).
- Testovi: dodaj guard u
tests/i provjeri lint. - Deploy: bumpaj
?v=i Ctrl+Shift+R.
02-nova-funkcionalnost.md) - ovaj recept je sažetak.