Case Study  /  002Shipped · 2025

Zain Middle East Properties

Full-Stack Real Estate Platform — Listings, Off-Plan & CRM.

Year2025
RoleFull Stack Engineer
Duration6 months
Stack7 tech
Visit Live
Zain Middle East Properties cover
01

Context

End-to-end real estate platform for a UAE brokerage: resale and rental listings, off-plan project catalogue, inquiry-to-booking CRM, a dynamic landing-page builder for paid campaigns, and a full admin suite with role-based access and audit logging — all on one Postgres backend.

02

The Challenge

Zain Middle East is an established UAE brokerage covering both secondary-market sales and off-plan investments across Abu Dhabi and Dubai. Before this engagement, listings were managed in spreadsheets, off-plan projects had no single source of truth, and every inbound lead landed in a shared WhatsApp group with no ownership or follow-up system. Paid campaigns sent traffic to a static brochure site with no landing-page logic and no conversion path. The business had real demand and experienced agents — but zero infrastructure to capture, qualify, or close that demand at scale.

03

Approach

I scoped the platform around three distinct user journeys that had to coexist on the same backend: the buyer browsing listings and making enquiries; the agent working their lead pipeline and scheduling viewings; and the marketing team launching campaign-specific landing pages without engineering support. Getting all three right meant the public-facing SEO site, the internal CRM, and the landing-page builder had to share one data model without one bleeding into another. tRPC gave us end-to-end type safety across all three surfaces from day one.

04

Key Decisions

  1. Dual inventory model: resale + off-plan as separate schemas

    Resale and rental listings share the property table — price, beds, baths, permit number, geo-coordinates. Off-plan projects are a separate schema: starting price, bedrooms range, payment plan, handover date, floor-plan PDFs, unit types. The two models are structurally different enough that collapsing them would have forced every field to be nullable and made filtering unreliable. Separate schemas keep both clean and independently queryable.

  2. Dynamic landing-page builder for paid campaigns

    The marketing team needed to run project-specific campaigns — each with its own hero copy, colour scheme, gallery, payment-plan section, Golden Visa section, and contact form — without dev involvement. I built a section-based config model stored as JSON in Postgres: each section (hero, about, gallery, floor plans, location, payment plan, contact) has an independent visible flag, colour overrides, bilingual text, and image slots. The admin UI maps directly onto that schema. A new campaign page can go live in minutes, not days.

  3. Inquiry-to-booking pipeline as a first-class data model

    Every contact form on the site — property pages, off-plan pages, landing pages, the homepage — writes to the same inquiry table with a typed source field and a status column (new → contacted → qualified → closed). A linked booking table stores the viewing date, time, and confirmation state. This means the team lead can open the admin and see exactly how many leads are stale, which properties are generating inquiries, and which agents are converting. Previously, that information lived nowhere.

  4. Developer and community directories as SEO assets

    Every major developer (Emaar, Aldar, Damac, etc.) and community (Yas Island, Saadiyat, Downtown) gets a dedicated slug-based page with its own metadata, canonical URL, and listing index. These pages rank for developer-specific and area-specific queries — "Emaar properties Abu Dhabi", "Saadiyat Island apartments for sale" — and funnel organic traffic into the listing and off-plan catalogue without paid spend.

  5. Role-based access with full audit logging

    The admin has three tiers: viewer, editor, and admin. Better Auth handles session management. Every write operation — create, update, delete — fires an audit log entry with the actor's user ID, name, email, role, IP address, and a JSON diff of old vs. new values. When a listing goes live or a price changes, there is a permanent, attributable record. This was a direct requirement from the business: the team needed accountability without micromanagement.

  6. tRPC for end-to-end type safety

    Every data query and mutation — public listing fetches, admin CRUD, landing-page config reads — goes through tRPC routers shared between server and client. The schema is the contract. Renaming a field in Drizzle breaks the tRPC router, which breaks the UI — caught at compile time, not in production. Given the volume of interconnected entities (properties, off-plan, developers, communities, inquiries, bookings, blogs, landing pages), type drift was the biggest long-term maintenance risk.

05

What Shipped

  • 01Dual inventory: resale/rental listings + off-plan project catalogue with payment-plan and handover details
  • 02Dynamic landing-page builder — each paid campaign gets its own SEO-optimised, fully configurable page
  • 03Inquiry-to-booking CRM: lead capture → qualification → booking scheduler, all in one pipeline
  • 04Developer and community directories with dedicated SEO pages for every entity
  • 05Role-based admin with full audit log — every create, update, and delete is attributed and time-stamped
  • 06Bilingual content infrastructure (EN / AR) with canonical signals and Open Graph per listing
  • 07tRPC end-to-end type safety across every admin and public data query
06

Outcomes

  • 01Entire listing and off-plan catalogue consolidated into one Postgres database — no spreadsheets, no duplication.
  • 02Marketing team ships new campaign landing pages independently, without developer involvement, in under 30 minutes.
  • 03Every inbound lead has an owner, a status, and a conversion path — pipeline visibility the business never had before.
  • 04Developer and community SEO pages created a passive organic traffic channel on top of the paid-campaign infrastructure.
  • 05Audit log gives management full accountability over every data change without slowing down agent operations.
07

Stack

Next.jsTypeScriptPostgreSQLDrizzle ORMtRPCSupabaseBetter Auth