Lucas Rezende
Back to all work

Case 01 · Institucional · 2026

WG7 Empreendimentos

B2B institutional platform with filterable catalog, granular per-city SEO and CMS-ready backend — built for organic lead capture in R$ 200k-2M/month corporate leases.

● LiveClient · Lyor Arquitetura
WG7 Empreendimentos
vertical
Corporate B2B leasing
coverage
Brazil

Context

Where
the problem lived.

01 · Context

The setup

Brazil's corporate real estate market moves billions in annual leases — 5,000 m² warehouses, corporate floors in São Paulo, custom build-to-suit for heavy industry. Each contract closes between R$ 200k and R$ 2M per month (USD 40k-400k). And the entire operation still runs on PDFs, spreadsheets and WhatsApp.

02 · Problem

The real pain

WG7 serves B2B clients across Brazil — corporate leases, build-to-suit (BTS) and asset management. Buyers at this level (industrial CFOs, expansion leads, real estate funds) want to research independently — see the portfolio, filter by city, compare areas and prices without calling anyone. With no digital storefront, WG7 was losing qualified leads before they even reached the first conversation.

03 · Response

How I responded

Institutional platform + filterable B2B catalog + CMS-ready backend. Granular SEO by city (high-intent commercial search becomes organic lead). The team updates properties without depending on a developer. Schema modeled from the start to become a white-label SaaS — if other corporate real estate firms ask for the same product, no rewrite is needed.

Stack

Every piece
with a reason.

No fashion picks. Each dependency here passed through a filter of maintenance cost, time-to-market and fit with a small team.

  • 01Next.js 15 (App Router)

    SSG/ISR for Google-indexable city pages, dynamic routes per property, Server Actions for the contact form

  • 02Supabase (Postgres + Storage)

    Full backend without a server. Properties table with RLS, Storage for photos. CMS is Supabase Studio itself — zero extra layer

  • 03next/image + Sharp

    Automatic WebP/AVIF, lazy loading. Real estate is photo-heavy — performance directly affects SEO

  • 04Tailwind + shadcn/ui

    Coherent design system (navy + gold palette), accessible components without reinventing the wheel

  • 05Leaflet

    Per-property mini-map with pin + location radius — premium signal for corporate clients

  • 06Vercel

    Global edge network, automatic ISR, per-PR preview deploys. Zero ops on my end

Architecture

How it all
talks.

The public site is ISR (revalidates every hour, or on Supabase webhook when content changes). City pages are generated statically from the cities table — every popular combination becomes an indexable URL. CMS is Supabase Studio, with roles that scope what each user can see. Leads jump straight into WhatsApp Business with pre-formatted messages.

01 · client

Public site

ISR · /imoveis/[city]

client

Supabase Studio

Property CMS (RLS)

client

02 · edge

Next.js App Router

SSG + ISR + Actions

edge

03 · data

Postgres

properties · cities · leads

data

Storage

Photos · documents

data

WhatsApp Business

Direct lead handoff

external

Google

Per-city indexing

external

Main flows

  • siteRSCnext
  • nextRLS readdb
  • nextimage proxystorage
  • studioadmin writedb
  • sitecontact deeplinkwpp
  • googleorganic trafficsite

Technical decisions

Seven choices
with explicit tradeoff.

Each one carries the problem, the options considered, the choice I made and the price I paid for it.

Decision 01

Lean schema, but modeled to become a SaaS

Problem

WG7 is today's client. The product could become a platform replicable for other corporate real estate firms (Cushman, Newmark BR, RFL). A schema locked into one brand is painful to rewrite later.

Options considered

  • a'Just for WG7' schema — fast now, painful refactor later
  • bFull multi-tenant on day 1 — unnecessary overengineering for a single client
  • cFunctional single-tenant, but tables modeled for extension (no brand hardcoding)

Choice

Properties, cities, categories and listings are decoupled. Brand config lives in a settings table (logo, palette, hero copy). Colors and tokens via CSS variables. Not multi-tenant yet, but it becomes one in 2-3 days if needed — add tenant_id, per-tenant RLS, multi-domain deploy.

Tradeoff accepted

Small modeling overhead now (~1 extra day on the MVP). In exchange: a real path to SaaS without rewrite. One additional real estate firm onboarded covers that investment with margin to spare.

Decision 02

WhatsApp Business as the primary lead handoff

Problem

Traditional form generates a lead that goes to email and dies. Corporate B2B real estate demands speed — first to call usually closes the visit. A lead waiting 30 minutes in an inbox is worth less than zero.

Options considered

  • aForm + email send — standard flow, high latency, no tracking
  • bForm + CRM integration (HubSpot / Pipedrive) — expensive at this stage, requires training
  • cMinimal form + WhatsApp deeplink with pre-formatted per-property message

Choice

Each property page has a 'Talk to WG7' button that opens WhatsApp Business with a pre-formatted message (property code, link back, city). The team answers directly, the lead never cools off. In parallel, a leads table records origin (city + category + listing_type) to feed funnel insight later.

Tradeoff accepted

No structured CRM at this stage — all communication lives in WhatsApp history. In exchange: zero friction for the client, response time in minutes, zero tooling cost, and basic lead tracking already shows which city converts best.

Highlighted implementation

One slice
that tells the story.

lib/properties/list.ts
typescript
// List properties from URL searchParams — type-safe + paginated
type Filters = z.infer<typeof PropertyFiltersSchema>;

export async function listProperties(
  filters: Filters,
  page = 1,
): Promise<{ items: Property[]; total: number; pages: number }> {
  const supabase = createServerClient();
  const PAGE_SIZE = 24;

  let query = supabase
    .from("properties")
    .select("id, title, slug, area_m2, monthly_rent, photo_url, city, state, category, featured", { count: "exact" })
    .eq("status", "active");

  if (filters.state) query = query.eq("state", filters.state);
  if (filters.category) query = query.eq("category", filters.category);
  if (filters.listing) query = query.eq("listing_type", filters.listing);
  if (filters.minArea) query = query.gte("area_m2", filters.minArea);
  if (filters.maxRent) query = query.lte("monthly_rent", filters.maxRent);

  const { data, error, count } = await query
    .order("featured", { ascending: false })
    .order("created_at", { ascending: false })
    .range((page - 1) * PAGE_SIZE, page * PAGE_SIZE - 1);

  if (error) throw new ListPropertiesError(error.message);

  return {
    items: data ?? [],
    total: count ?? 0,
    pages: Math.ceil((count ?? 0) / PAGE_SIZE),
  };
}
Filters arrive validated by Zod and map directly into the query builder. Every combination becomes a shareable, Google-indexable URL.

Technical metrics

Numbers
that matter.

  • Vertical

    B2B

    corporate leasing, build-to-suit

  • Client autonomy

    ~95%

    ops that run without dev help

  • Time to publish a property

    ~2 min

    via Supabase Studio

  • Infra cost / month

    $0

    Vercel + Supabase free tiers

  • Status workflow

    4 states

    draft · active · featured · archived

  • Next step

    Multi-tenant

    schema ready to go white-label

Retrospective

What I'd do
differently today.

Decisions I'd reverse knowing what I know now. Not regret — the kind of learning that changes the next project.

  1. 01

    Structured lead tracker from day one

    Leads go 95% to WhatsApp and live in chat history. Functional for this phase, but loses valuable funnel insight — which city converts best, which category attracts leads but doesn't close, which button copy performs better. The leads table exists, but with too few fields. Next iteration: UTM + origin (applied filter) + minimal status (new, in conversation, won, lost).

  2. 02

    Specialized image CDN before scaling the portfolio

    Supabase Storage handles the current volume (15+ cities, ~100 properties), but optimization is on-demand via next/image — first request per new photo pays 800ms. When the portfolio hits 500+ properties, Cloudflare Images or Bunny CDN with upload-time transforms become the right call. It's a timing decision, not an architectural one.

  3. 03

    Single parametrized Map component from the first use

    Leaflet was added case-by-case as new places asked for a map. Today there are 3 spots with slightly different configurations. Should have been one parametrized Map component from the first instance — a refactor sprint will fix it, but the cost was zero if done directly.

  4. 04

    A/B testing on the main CTA copy

    The contact button is fixed: 'Talk to WG7'. For corporate B2B, copy matters: 'Schedule a visit', 'Negotiate lease', 'Request a quote'. I'll run a simple test with a per-city flag — measure click rate without investing in heavy tooling. Lack of discipline measuring this early on.

Live platform

Live.

Open in new tab
wg7empreendimentos.com.br
Loading live site…

Real site, embedded live — not a screenshot

WG7 Empreendimentos — Case study · Lucas Rezende