Panel klienta VulCode
The client panel for our own brand: projects, billing, an affiliate program with automatic commission payout and support tickets. Role-based access control, a multilingual interface and real data - not a mockup. The same code behaves like a native app on a phone.
The client panel for our own VulCode brand: projects, billing, tickets and an affiliate program in one window, instead of emails bounced back and forth. Commissions counted in whole grosze and paid into a wallet automatically, role-based access control, and the same code that runs on a computer installs on a phone and behaves like a native app.
Overview
VulCode is our own brand, so we built this panel for ourselves and that turned out to be the hardest possible client. When you build for yourself there is no one to hide behind: every shortcut, every number that does not add up, comes straight back to you. So from the start this project was meant to be the template for what a client panel looks like for us, not a stopgap for internal use.
The task sounded simple: give the client one place where they see everything that concerns them and handle matters that used to need an email themselves. In practice it meant wiring several worlds together at once - project state, invoices, support tickets and an affiliate program counting real money - into one coherent screen that works the same on desktop and in a pocket. This case study shows where the real traps were and how we got around them.
The end of "where is my invoice" emails
Before the panel, every question about a project stage or a document ended in an email, and the answer depended on who read it and whether they remembered the context. That does not scale even at a dozen clients: the same information lived in one person's inbox, and the client waited instead of simply looking.
The panel flips that. The client logs in and immediately sees their projects with work status, a list of invoices with what is paid and what is due, and the history of every ticket they ever opened. Nothing has to be dug out of email, because the source of truth is one view, not someone's memory.
Less waiting on both sides
The effect is felt on both sides. The client stops waiting on a human to learn things the system already knows. The team stops answering the same questions for the hundredth time, because the answer is already on screen before anyone types it.
Steps to check a project status (illustrative)
Money counted in grosze, not in approximation
An affiliate program looks innocent until you start counting commissions on amounts with grosze. That is where the classic dramas begin: ten percent of 19.99 zl computed on a floating-point number can give three slightly different results on the commission list, in the wallet and on the payout, and then balances stop adding up and nobody trusts the panel.
So across the whole panel we keep money as whole integers in grosze and compute every commission on ints. There is never a fraction of a zloty here that could round one way once and the other way next time. One operation, one round-down rule, the same result everywhere that amount shows up.
We keep the commission rate in basis points (bps), not in percent with a decimal. Ten percent is 1000 bps, not 0.1 stored as a float. That way the whole commission math runs on integers, from the rate to the result, with no spot where a rounding error could sneak in.
Who sees what and why
A client panel shows money and documents, so the question of who is allowed to see something is not cosmetic. The client is to see only their own data, the team only what it is entitled to, and the line between them cannot depend on whether the front end happened to hide a button.
We built it on role-based access control and secure authentication, but the key decision is architectural: the scope of what can be seen and done is checked by the backend at the API entry, not by the interface. The front end can show or hide whatever it likes, but if a request touches someone else's data, the API rejects it before it ever reaches the database.
It is the same principle we apply everywhere: you guard the boundary where outside data comes in, that is the API, and you do not count on the user not swapping an id in the URL.
The same code on the desk and in the pocket
The client is not always at a computer. They check an invoice on the tram, react to a ticket notification from their phone. Building a separate mobile app for that would mean a second codebase to maintain and a guarantee that sooner or later the two versions drift apart.
Instead the panel is a web app that installs on a phone as a PWA, with a manifest and a service worker. The same code, the same backend, the same commission logic. On a computer it is a full desktop, on a phone an icon on the home screen and a window without a browser bar, behaving like a native app.
Real data from day one
The easiest thing is to build a panel that looks good on numbers prepared in advance. It looks good until someone clicks and it turns out there is nothing underneath. We went the other way: from day one the panel stands on a real backend, and every screen shows what is actually in the database.
That choice costs more up front, because you need a working API before anything is drawn. But it pays off immediately, because there is no "we will connect the data later" moment where all the assumptions that did not survive contact with reality usually surface. What you see in the panel is what the system actually knows.
Stack
| Layer | Technology |
|---|---|
| Frontend | React + TypeScript on Vite |
| Backend | Node + TypeScript, SQLite |
| Access | roles and authentication on the API side |
| Money | whole integers in grosze, rates in bps |
| i18n | one PL and EN translation source |
| On phone | PWA with a manifest and a service worker |
What the finished product actually delivers
The client gets one place where they see their state and handle matters that used to need an email to the team. They come in, check a project, download an invoice, reply in a ticket, look at the commission balance from the affiliate program, and if they happen to be on the move they do the same from a phone, because it is the same app. None of it depends on whether someone from the team is at the inbox.
On our side the gain is just as concrete. The affiliate program runs without anyone re-adding anything by hand, because integer-based code guards it, not a spreadsheet someone has to reconcile. Balances match, because there is no place a rounding error could be born. The team gets back the time that used to go into answering the same questions.
Most importantly, this panel became our template. The core it stands on - roles, wallets, commissions, tickets, i18n, app mode - is clean enough that it moved onto other brands without a rewrite. We started with the hardest client, ourselves, and out of it came an architecture we later only dressed in new colors.
More projects
More work from the same category - see how we tackle similar challenges.
Have a similar project?
Get in touch - a quote is free and comes back within an hour.




