Mobile AppAntino Labs
Offers Dashboard & Rewards System
The rewards half of NeoZAP: an offers console where the team publishes and expires offers and sets the wheel configuration, and the in-app surface that reads it. Before this, changing an offer meant a developer running SQL.
01
The problem
Offers and prize configuration lived in the database, and changing any of it meant a developer writing SQL against production. Every campaign needed an engineer, every edit was a hand-written query, and nobody outside the team could see what was currently live without asking.
02
The approach
One console over the rewards APIs, and one in-app surface reading the same data. The team creates and expires offers from a form instead of a query, sets which are live and when, and configures the wheel segments and their values. The app side renders that catalogue, the offer detail and the claim, so what is published is what users see without anything being rebuilt.
Overview
- 2
- surfaces, one API
- 0
- SQL statements to publish an offer
- 12
- technologies
03
Key decisions
Offers edited through a console
over A developer updating rows directly
Every offer change was a hand-written SQL statement against production, which put an engineer in the middle of marketing work and put live data one typo away from a mistake. A form with validation over an API removed both problems.
Wheel configuration as data
over Prize values fixed in the code
The wheel is the part the team most wants to adjust, and the part that is most awkward to change through a deploy. Keeping segments and values in the console meant tuning the wheel never touched the app.
PrimeReact with react-hook-form for the console
over Building the table and form layer from scratch
The console is a data grid and a long validated form. Using a component library for the table and a form library for the state left the effort on the offer rules and the wheel configuration, which is where the domain actually was.
The in-app surface reading the same API as the console
over A separate read path for mobile
Two paths mean two places for the definition of a live offer to drift apart. Sharing the API meant publishing was the only thing that changed what users saw.
04
What I built
Offers console
React · PrimeReact
The offer catalogue in one list, with search and category filters, so the team can see what is live without asking.
Offer editor
react-hook-form · Validation
Offer details entered and validated in a form rather than typed into a SQL statement.
Wheel configuration
React · react-hook-form
Segments and their values set from the console, so changing the wheel does not involve the app.
Rewards APIs
Node.js · Express · PostgreSQL · Sequelize
One contract behind both the console and the in-app surface, so a publish is the only change.
In-app offers list
Next.js · REST API
Category-filtered catalogue in the app, reading published offers directly.
Offer detail and claim
Next.js · REST API
Offer detail with the claim action, returning the coupon code on success.
Filter state in the URL
Next.js routing
Sort and filter survive a refresh and a share, instead of resetting with component state.
05
The outcome
Offers and wheel configuration are managed from the console rather than through the database. The team publishes and expires campaigns without an engineer, and the in-app surface picks the changes up directly.
Stack
- React
- Next.js
- TypeScript
- PrimeReact
- react-hook-form
- Validation
- Redux Toolkit
- Node.js
- Express
- REST API
- PostgreSQL
- Sequelize





