Multi-service ordering and delivery marketplace
Swyftng
I have built Swyftng's web platform since the MVP in May 2024, through five major versions. Version 5 has just shipped.
- Role
- Frontend developer
- Period
- May 2024 – present
- Live
- swyftng.com
- Tools
Tools used: React, TypeScript, Vite, React Query, Zustand, React Hook Form, Zod, Tailwind CSS


The problem
Swyftng lets customers order from restaurants, supermarkets, local markets and home care services in one place, book a rider for any parcel, and pay with gift cards and earned credit. Vendors, riders and Swyftng's own staff run their side of each order, and the platform has grown by a new kind of order with almost every version.
What I built
- Consumer, vendor, rider and admin interfaces in one React app.
- The full order workflow, from payment through processing to delivered, for every kind of order the platform sells.
- Credit and rewards end to end: gift cards, referrals, feedback rewards, cashback and special packages, from the customer's wallet to the admin tools that manage and track them.
- Cursor pagination on the credit management tables, using the pattern I first designed on the ambulance platform.
Five versions, one codebase
- v1Food ordering and delivery, with consumer, vendor, rider and admin interfaces.
- v2Errands from supermarkets, local markets and drink shops, fulfilled by Swyftng's own marketers.
- v3Swyftng Express+: book a dispatch rider for any parcel.
- v4Personal and home care services, booked as appointments.
- v5Credit management (gift cards, referrals, cashback and feedback rewards) and special packages.
One app, every interface
Swyftng serves customers, vendors, riders and admins from one React application.
Each interface has its own layout, routes and context, and they share one onboarding flow that signs a user in and sends them to the right interface for their role. A session that lands in the wrong interface is signed out rather than shown someone else's screens. Marketers are Swyftng staff who shop the errands customers order, and they work from a separate mobile app. My side of that is where it meets the web: the customer placing the errand, and the admin seeing and managing the marketer's orders alongside every other kind. Shared building blocks live in one place: the API client, the query hooks and the UI kit. A fix to how errors are handled reaches every interface at once.
Growing through five versions without a rewrite
Almost every version added a new kind of order: restaurant meals, market errands, parcel dispatch, home care appointments, gift cards, then special packages.
Each one touches the cart, checkout, order history and the admin. Rewriting for each was never an option on a live platform, so the work was making every layer accept a new order type without breaking the old ones. The customer's order history card works out which kind of order it is showing and renders the right details, prices and actions. The admin's order view does the same, and product management handles restaurant, market, service and special-package products, each with its own create and edit rules. By v5 checkout had grown into one 1,672-line component, plus a separate guest cart and two toast libraries. I rebuilt it as a three-screen wizard (cart, checkout, then transaction PIN) driven by a single order context that owns the cart query, the delivery and payment choices and every mutation. If the customer has no PIN yet, creating one is its own small state machine: create, confirm, verify by OTP, then enter. Redeeming a gift card and applying earned credit then went in as one component each.
Admin roles, permissions and an audit trail
The super admin invites other admins, creates roles from any set of permissions, assigns a role to each admin, and can suspend or restore an account.
The admin list shows who is online, who is offline, when each admin last signed in, and who has been invited but hasn't joined yet. Every change an admin makes is recorded: who made it, which record it touched, and the old and new value of each field. A product's price, discount or approval status can be traced to the person who changed it and when, and each admin's account page lists their recent actions.
A colour system admins can't break
Admins design gift cards and special-package cards themselves, so colour had to be safe in their hands.
A gift card's design is four colours. I curated 30 named combinations that work together, and the backend stores only the four hex values. The names live only in the frontend, so they can be renamed without touching the API. When an admin picks a theme, each option shows whether it is already in use and on how many cards. When an admin edits a card, the right theme is pre-selected by matching the stored colours back to a name. A special package needs only one colour. The card's grid lines are derived from it, same hue and slightly darker, so no choice can clash. The card works in HSL internally and converts to hex only at the API boundary, and it still reads an older colour format the backend once stored, so old packages keep rendering.
SUPA · in use on 2 cards
What the API receives
{
"name": "Birthday treat",
"value": 20000,
"design": {
"colors": {
"primary": "#EDDEE3",
"secondary": "#F6EFF1",
"accent": "#850E35",
"text": "#850E35"
}
}
}Only the four colours are stored. When this card is opened for editing, they match back to SUPA, so the picker pre-selects it.
When the data disagrees
In a customer's order history, expanding a special-package order showed the wrong price for its items: ₦1,500 where the customer had paid ₦15,000.
The screen was reading the product's base price. The backend sends several price fields for each cart item, and they don't agree: one is sometimes a spurious 0, and the base price ignores the variation the customer chose. Instead of guessing which field to trust, I checked each one against 100 real orders and counted where the line totals added up to what was charged. Regular products reconciled best on the product price, dynamic packages on the selected variation's price, and static packages on the base price. I reordered the fallback chain to match. That fixed 5 orders with no regressions. Two orders matched no combination of fields at all. I sent them to the backend team with the evidence, because the real fix is one authoritative charged price per item. Until then, my chain is a documented workaround.
Breadcrumbs that follow the admin anywhere
Admins investigating a problem jump between records: from an order to the customer who placed it, to the vendor, to one of the vendor's products, and back.
I built each detail view (order, account, business, product, service) as a self-contained component that takes the breadcrumb trail it was opened from, adds its own crumb, and passes the longer trail to whatever it opens next. Because every view works the same way, they nest in any order and work from any admin section, and clicking any crumb returns to that level. The trade-off is that the trail lives in component state rather than the URL, so a page refresh returns to the list. Moving it into the URL is the next step.








Newman Adesan