Full-stack
reagiraj.ba: a lung cancer risk check for the EU LIFEGATE project
More than 2,300 completed assessments in the first weeks after launch: a lung cancer risk check for SKB Mostar and the EU LIFEGATE project, built with React, Node.js and MongoDB.
- Client
- SKB Mostar (University Clinical Hospital Mostar), within the EU Interreg IPA project LIFEGATE
- Delivered by
- ANODA
- Year
- 2026
- Role
- Technical partner for the web application (activity A1.2): platform, admin panel and analytics
- Product language
- Bosnian / Croatian


Developed within the LIFEGATE project, co-funded by the European Union through the Interreg VI-A IPA Croatia, Bosnia and Herzegovina, Montenegro 2021 to 2027 programme.
At a glance
| Client | SKB Mostar (University Clinical Hospital Mostar) |
|---|---|
| Programme | Interreg VI-A IPA Croatia, Bosnia and Herzegovina, Montenegro 2021 to 2027, co-funded by the European Union |
| Project | LIFEGATE (HR-BA-ME00489) |
| My role | Technical partner for the web application (activity A1.2): platform, admin panel and analytics. |
| Timeline | Development December 2025 to May 2026, public launch on 11 August 2026 |
| Platform | Public web app, API and admin panel |
| Users | The general public, designed with older users in mind; the hospital team in the admin |
| Status | Live |
The problem
Lung cancer is often found late, when treatment options are limited. The LIFEGATE project needed a simple public tool that helps people understand their own risk and encourages those at higher risk to see a doctor, across a region where many of the people who need it most are older and not used to online forms.
User journey
Free, anonymous and about two minutes long. Every step was designed with older users in mind: large answer cards, short steps and plain language.
- 1

Landing
What the check is, how long it takes, and that it is anonymous.
- 2

Basic data
Age, canton, city, weight and height (BMI is calculated automatically), personal and family history.
- 3

Smoking and symptoms
Smoking status, years and cigarettes per day (pack-years are calculated), symptoms and lung disease.
- 4

Environment
Living environment and occupational exposure, such as asbestos.
- 5

Result
A risk level with a clear recommendation and the detected factors.
- 6

Where to go
People at higher risk get the nearest pulmology referral location for their area and a referral code to verify at the institution.
Screenshots from production. Answers are non-personal samples and nothing was submitted. Clinical point weights and doctors' details are hidden.
The scoring algorithm
The model was defined together with the clinical experts on the project; my job was to implement it exactly, test it against edge cases and keep it explainable.
Symptomatic / urgent: clinical assessment needed
A safety rule, not a screening score. Coughing up blood, or several serious symptoms at once.
Additive screening score
Points across domains: age, smoking (status and pack-years), lung disease, family history of lung cancer, personal history of cancer, asbestos, other occupational exposure, living environment, passive smoking.
Booster check
20 or more pack-years plus at least one strong amplifier (asbestos, COPD, first-degree family history, or previous cancer) means at least High.
| Level | Score | Recommendation |
|---|---|---|
| Low | 0 to 7 points | Education, quitting smoking, no routine screening |
| Moderate | 8 to 15 points | Consider a chest X-ray, with AI support where available, and/or low-dose CT according to guidelines and individual risk |
| High | 16 points or more | Screening with X-ray plus AI and low-dose CT, and a pulmology examination |
| Urgent | Alarming symptoms | Clinical assessment needed, regardless of the score |
Pack-years
Pack-years = (cigarettes per day ÷ 20) × years smoked
One pack a day for 20 years is 20 pack-years, and so is two packs a day for 10 years. The app calculates it from the answers, so nobody has to do the maths.
Architecture
Browser
React single-page app at reagiraj.ba
Admin panel
admin.reagiraj.ba, cookie-based authentication
API
Node.js and Express at api.reagiraj.ba
Database
MongoDB Atlas
Analytics
GTM and GA4, in the browser only
The risk level never leaves the app
Data model
| Field group | Examples | Why it exists |
|---|---|---|
| Demographics | Gender, age, canton, city | Risk factors and geographic analytics for the hospital team |
| Body measurements and BMI | Weight, height, calculated BMI | Shown in the result and used in the analytics |
| Smoking and pack-years | Status, years, cigarettes per day, calculated pack-years | The strongest risk factor in the model |
| Symptoms and history | Symptoms, lung disease, personal and family history | Safety override and screening score |
| Exposure | Living environment, asbestos, occupational exposure | Environmental and occupational risk factors |
| Result | Score, level, detected factors, recommendation | What the person sees, and what the clinic can verify |
| Referral | Referral code | Verifying results at the institution |
| Privacy | IP address hash, excluded-from-analytics flag | Abuse protection without storing IP addresses, and clean statistics without test submissions |
Translations are managed in the admin, so the hospital team can change wording without a release. Consent is handled with Google Consent Mode: Cookiebot can be switched on from the admin, with a built-in consent banner as the default.
Admin panel
The hospital team works in an admin panel with five sections: Dashboard, Analytics, Applications, Translations and Settings. The analytics views cover demographics, geographic breakdown, risk distribution, cross-factor analysis and the daily trend.
Completed
1,234
Today
56
High or urgent
28%
Risk distribution
- Low: 38%
- Moderate: 34%
- High: 21%
- Urgent: 7%
By canton
- Canton A
- Canton B
- Canton C
- Canton D
Daily trend
Measurement with care
Because this is a health tool, I designed the analytics around one rule: nothing about a person's health may leave the app.
| Decision | Why |
|---|---|
| Risk level is never sent to analytics | GA4 knows that an assessment was completed, never what the result was |
| Steps are tracked from the app's code, not from the page HTML | A single-page app without URL changes needs events pushed from code |
| IP addresses are stored only as a hash | Abuse protection without keeping personal data |
| Test submissions can be excluded | Statistics stay clean for the hospital team and the project reports |
| No advertising profiles | Nothing about a person's health is shared with any third party. |
Results
Figures from the project reports of 27 August and 10 September 2026.
2,300+
completed assessments in the first weeks after launch
~30%
assessed as high or urgent risk
11,000+
visits
- Dec 2025Development starts
- May 2026Platform delivered
- Jul 2026Analytics and privacy review
- 11 Aug 2026Public launch
- Sep 20262,300+ completed assessments
What I learned
- Implementing a clinical model means implementing it exactly and testably: every rule covered by edge-case tests, and every result explainable to the person who gets it.
- Analytics around health data has to be designed from day one. Deciding what must never be tracked is as important as deciding what to measure.
- Single-page app tracking needs hooks in the code, not CSS selectors. The app knows which step a person is on; the DOM does not.
Stack
| Layer | Technology |
|---|---|
| Frontend | React single-page app |
| API | Node.js and Express |
| Database | MongoDB Atlas |
| Admin | React admin panel with cookie-based authentication |
| Analytics | Google Tag Manager, GA4, Google Consent Mode |
