All projects

DUBTEL AI Portal

The white-label Next.js portal where businesses manage AI voice agents, phone numbers, billing, and users, with role-aware navigation and a System Admin console across every tenant.

Role
Lead frontend engineer
System
DUBTEL AI, the portal on top of the multi-tenant FastAPI backend
A dashboard with a tenant switcher, agent tiles, and an invoice

Results at a glance

  • White-label branding: tenant logos, custom-domain login screens, and a dynamic favicon.
  • A System Admin console with overview, tenants, users, billing, pricing, and settings, plus a global tenant switcher.
  • Full CRUD for AI agents, phone numbers (with port-in requests), sub-accounts, and calendar events.
  • Billing with invoice detail pages and PDF downloads, and an activity log for each role.
  • A public Help Center, and an SSO button wired to the backend's SAML flow.

Overview

The portal is where every DUBTEL AI user works: platform staff, resellers, their business clients, and read-only users. I built it in Next.js with TanStack Query for server state and Zustand for client state, on top of the multi-tenant FastAPI backend I also built.

One portal, many brands

Resellers sell DUBTEL AI as their own. The portal resolves the tenant from the domain, so the login screen already shows the reseller's logo and name before anyone signs in. After login, the sidebar, favicon, and invoice headers all use the tenant's logo, which is uploaded through the portal and stored in object storage.

Role-aware everything

Four roles see four different products:

  • System Admin gets a console for the whole platform: overview, tenant management, users across tenants, billing, pricing, and named settings. A global tenant switcher narrows every list to one reseller, and lists show visible tenant group headers when viewing all tenants.
  • Tenant Admins manage their business clients and pricing.
  • Account Admins manage their own client's agents, numbers, and users.
  • Account Users can view but not change anything.

Navigation and write actions are gated by role in the UI to match the API's permissions. The API still enforces them.

Billing and invoices

Billing lists support view and download on every row. The invoice PDF took three attempts:

  1. html2canvas failed on the Tailwind v4 styles, because it can't parse modern lab() colours.
  2. Drawing the invoice with jsPDF's API avoided that, but was fiddly to keep in sync with the on-screen design.
  3. The final version asks the server for the PDF, so the browser just downloads a file and there's no print dialog.

The browser is a poor place to generate documents. The server has the data and a real rendering engine.

Deploys

The portal ships through GitLab CI, with qa deploying to QA and master to production. When the target servers moved to plain Apache web roots, I switched the build to Next.js static export. The one dynamic route (billing/[id]) became a static page that reads the ID from the query string, and static export is only enabled in CI, so local development keeps the full dev server.

Also shipped

  • A public Help Center with role-specific guides
  • Activity Log pages for System, Tenant Admin, and Account Admin
  • A redesigned login page and light-mode default based on feedback
  • Consistent date formatting, centralized in one helper after a round of "Jul 31, 2026" vs MM/DD/YYYY fixes

What I took from it

  • Mirror the server's permission model in the UI, and never rely on it. The API is still the gate.
  • A white-label product needs branding resolved from the domain before the user logs in.
  • When a browser library can't handle modern CSS, move the job to the server.
Henry Iddirisu

Henry Iddirisu

AI product engineer · Accra, Ghana · Remote

More projects

Next.jsReactTypeScript

Checkout Payments and Student Billing

Role: Full-stack engineer (payment step; billing API extensions; student and…

The payment step of a European e-learning checkout (Stripe, Klarna, Alma, PagoLight, SeQura), plus the billing API and student bil…

LaravelLaravel ReverbPHP

Lesson Whiteboard

Role: Full-stack engineer (sole developer across all three codebases)

A live whiteboard for online language lessons, built end to end across a Laravel API, Reverb WebSockets, the tutor app, and the st…