Back to work
2026·Corporate · UAEPRODUCTION

WINDOW LAND

Premium glass & aluminium for Dubai's skyline.

Full-stack monorepo for a Dubai-based premium glass and aluminium installation company. Next.js marketing site (25 pages) + admin panel, Node.js Express CMS API, and a Python FastAPI quote calculator — all wired through a Turborepo monorepo with CI/CD.

WINDOW LAND — live site screenshot
Duration
10 weeks
Team
Solo full-stack
My role
Lead full-stack engineer
Outcome
Years experience
8+
Services offered
19+
Projects delivered
100+
UAE Licensed
DED
The story

From brief to production system.

Challenge

The client had no online presence and was losing leads to competitors who showed up in 'glass installation Dubai' searches. They needed a site that visually matched their premium positioning, an admin panel non-technical staff could update without engineers, a quote calculator for inbound leads, and serious UAE-market SEO.

Solution

Architected as a Turborepo monorepo with three deployable services: a Next.js 14 frontend (25 pages, animations, admin panel with JWT auth), a Node.js Express API for content management, and a Python FastAPI microservice for quote calculations. GSAP and Framer Motion for the luxe feel. Full LocalBusiness JSON-LD and geo-targeting for UAE search.

Outcome

Production-ready site at window-land.vercel.app awaiting windowland.ae DNS cutover. Admin team can update services and project galleries without a developer. Quote engine and WhatsApp lead capture wired and tested.

Process · 10 weeks

How it shipped, week by week.

Week 1–2
01 / 5

Discovery

Calls with the Dubai-based CEO. Mapped services, gathered brand assets, wrote the IA and SEO keyword plan for UAE.

Week 3–5
02 / 5

Frontend

Monorepo scaffolding, Next.js App Router, design system, all 25 marketing pages with GSAP animations on the hero and section reveals.

Week 6–7
03 / 5

Admin + API

JWT auth flow, role-based admin panel, Express CMS API with MongoDB models, image upload pipeline through Cloudinary.

Week 8
04 / 5

Quote engine

Python FastAPI microservice for square-meter quote calculations, with Postgres for pricing tables versioned over time.

Week 9–10
05 / 5

SEO + ship

LocalBusiness JSON-LD, sitemap, OG images, GitHub Actions CI workflows, Vercel + Railway deploy guide handed to client.

Inside the system

What it does. How it's built.

Features

  • 25-page marketing site with cinematic Dubai imagery
  • Admin panel with JWT auth and role-based access
  • Python-powered quote calculator (m², material, finish)
  • Project gallery with category filters
  • Service catalog organized by vertical
  • Multi-channel contact (WhatsApp, email, phone)
  • LocalBusiness JSON-LD for Google rich results
  • Cloudinary-backed media pipeline
  • GSAP scroll animations + Framer Motion page transitions

Architecture

  • 01Turborepo monorepo with shared TypeScript types
  • 02apps/web — Next.js 14 App Router + Tailwind
  • 03apps/api — Node.js Express + MongoDB Atlas
  • 04apps/python — FastAPI quote service + Neon Postgres
  • 05Upstash Redis for rate limiting + session cache
  • 06Cloudinary for image CDN with automatic transforms
  • 07GitHub Actions CI: typecheck, lint, build per app
  • 08Vercel for frontend, Railway for backend services
Stack
Next.js 14ReactTypeScriptTailwind CSSGSAPFramer MotionTurborepoExpressFastAPI (Python)MongoDB AtlasNeon PostgresUpstash RedisCloudinarySendGridWhatsApp Cloud APIGitHub Actions
From the codebase

Annotated excerpts.

01 · FastAPI endpoint that prices a glass-and-aluminium job. Pricing tables versioned in Postgres so historic quotes stay reproducible.
apps/python/quote.pypython
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from db import get_pricing_for

app = FastAPI()

class QuoteRequest(BaseModel):
    service: str         # e.g. "curtain_wall"
    width_m: float
    height_m: float
    finish: str          # "powder_coated" | "anodised" | "polished"
    glass_type: str      # "tempered" | "laminated" | "low_e"

@app.post("/quote")
async def quote(req: QuoteRequest):
    area = req.width_m * req.height_m
    if area <= 0 or area > 200:
        raise HTTPException(400, "Area out of range")

    pricing = await get_pricing_for(req.service)
    if pricing is None:
        raise HTTPException(404, "Service not priced")

    finish_mult = pricing["finish_multipliers"][req.finish]
    glass_mult = pricing["glass_multipliers"][req.glass_type]

    base_aed = pricing["base_aed_per_m2"] * area
    total_aed = round(base_aed * finish_mult * glass_mult, 2)

    return {
        "service": req.service,
        "area_m2": round(area, 2),
        "total_aed": total_aed,
        "pricing_version": pricing["version"],
    }
02 · JWT verification middleware for the admin panel. Role-checking is baked in so route handlers stay tiny.
apps/web/lib/auth/jwt.tstypescript
import { jwtVerify, type JWTPayload } from "jose";
import { cookies } from "next/headers";

const SECRET = new TextEncoder().encode(process.env.JWT_SECRET!);

export type Role = "admin" | "editor" | "viewer";

export interface AdminClaims extends JWTPayload {
  sub: string;
  role: Role;
}

export async function getAdmin(required?: Role): Promise<AdminClaims> {
  const token = (await cookies()).get("admin_token")?.value;
  if (!token) throw new Error("Not authenticated");

  const { payload } = await jwtVerify<AdminClaims>(token, SECRET);

  if (required && rank(payload.role) < rank(required)) {
    throw new Error(`Requires ${required}, has ${payload.role}`);
  }
  return payload;
}

function rank(r: Role) {
  return { viewer: 0, editor: 1, admin: 2 }[r];
}
What the client said
Ali built our entire web presence from scratch — three services in one repo, all working together. The admin panel means my team can update content without ever calling a developer. Exactly what we needed to compete in the Dubai market.
MW
Muhammad Waqas
CEO · Window Land Glass & Aluminum Installation Co. LLC, Dubai
Other projects

Continue browsing

Have a project like this in mind? Let's talk.

Send me a brief and I'll respond within 24 hours.

← Home© 2025 Ali RazzaqContact →