Skip to content
abdulmazid.tech
CRM / ERPUnited Arab Emirates

A storefront and an ERP in one bilingual system

A Dubai and Sharjah furnishing business running on WhatsApp threads and spreadsheets. Built one bilingual Next.js system that serves customers at the front and runs leads, quotations, orders and invoicing at the back.

Visit the live siteClient work — repository is private
Market
United Arab Emirates
Type
CRM / ERP
Delivered
2026
Duration
12 weeks

Measured results

Each figure records where it came from. Nothing here is an estimate or a round number chosen because it reads well.

1
REST endpoints in the entire application
Everything else is a Server Action; the single route signs Cloudinary uploads
0
production dependency advisories
npm audit --omit=dev, recorded in the project audit
68
catalogue records migrated and verified
Categories and subcategories, with 59/59 images confirmed reachable
3
staff roles with separated data access
Admin, sales agent, operations — enforced at the payload, not the UI

The brief

Al Marouf sells made-to-measure curtains, custom sofas, wallpaper and carpets across Dubai and Sharjah, with installation. Two problems needed solving at once: a public storefront that could be found in both English and Arabic, and an internal system to replace running the business out of a messaging app.

Rather than two products, it is one Next.js application. The public site and the /admin ERP share the same content model, so a product added to the catalogue is immediately quotable, and a quotation that is accepted becomes an order without anyone retyping anything.

What was wrong

Operations lived in WhatsApp. Leads, measurements, quoted prices and delivery dates were spread across message threads. Nobody could answer "what is in the pipeline this month" without scrolling.

Quotations were editable after acceptance. Whatever was agreed could drift, with no record of what the customer actually said yes to.

The market is genuinely bilingual. In the UAE, English-only loses customers and machine-translated Arabic loses trust. Right-to-left is a layout requirement, not a stylesheet toggle.

Customers wanted to check their own order status, but a quote reference in a URL is guessable — so any tracking page is an IDOR waiting to happen.

What I did

One data model, two faces. The storefront and the ERP read the same tables. Every customer-facing field exists as a paired En/Ar column, so translation is a content responsibility rather than a runtime guess.

Immutable quote snapshots. Accepting a quotation copies its line items and creates exactly one order, one work order and one installation task. What the customer agreed to cannot be edited afterwards.

Gapless invoice numbering. Invoice numbers are generated inside a database transaction, so two staff members creating an invoice in the same second cannot produce a gap or a duplicate. Gaps in an invoice sequence are the sort of thing that only becomes a problem during an audit.

Phone-verified customer portal. Order tracking requires an SMS one-time code, and every portal operation re-checks that the session is scoped to that specific quote — the authorisation is on the operation, not on the route.

Role-separated staff access. Three staff roles, and records outside a role are not included in that user's page payload at all, rather than being hidden in the interface.

A VAT fail-safe. VAT is only ever applied when a verified tax registration number is configured. The system will not invent a tax line.

Built with

Next.js 16 · TypeScript · PostgreSQL · Drizzle ORM · next-intl · Cloudinary · Resend · Playwright

More work

Business siteAustralia

Cutting a roofing company’s page weight by two thirds

A Perth roofing business with a heavy single-page app that search engines could barely read. Rebuilt the delivery layer around prerendered HTML and cut the main JavaScript bundle by 68%.

−68% main JavaScript bundle

React · Vite · Node.js
MarketplaceBangladesh

A services marketplace, then the same codebase sold twice

A multi-vendor marketplace with three role tiers, real-time chat that converts into orders, and payment capture with webhook reconciliation — later white-labelled and deployed for a second client.

2 production deployments from one codebase

React · Node.js · Express

Have something like this in mind?

A short call to work out what it would actually take. Free, and you get a written scope and price before anything is committed.