Skip to content
Nafiz Iqbal
Projects

Project management SaaS for interior designers

Foundry Hub

Interior designers ran projects out of spreadsheets, PDF email chains, and a dozen vendor tabs. Foundry Hub puts sourcing, spec schedules, and client sign-off in one place.

Ownership
Client work
Organisation
Interior design studios
My role
Product designer and full-stack engineer
Status
Built 2025
  • TypeScript
  • Next.js
  • Node.js
  • PostgreSQL
  • Background jobs
Foundry Hub designer dashboard with project stats and pending client approvals

Overview

Interior design projects run on product selections: sofas, lights, fabrics, fixtures, each from a different vendor. A designer's selections lived in three places at once: a spreadsheet, a folder of PDFs emailed back and forth, and a dozen vendor tabs. Nothing was the single source of truth.

Foundry Hub replaces all three. Designers source products, organise them by room, build the specification schedule they hand off, and collect client approvals in one platform.

Role and ownership

What I personally did

  • Product design for the designer workspace and the client review portal.
  • Next.js and TypeScript front end, Node.js API, and PostgreSQL schema.
  • Background job pipeline that imports products from pasted vendor URLs.
  • Schedule builder with drag-to-reorder rows and one-click PDF export.

Constraints

  • Vendor sites are inconsistent and some can't be read automatically at all.
  • Two very different users: designers who live in the tool all day, and clients who open it a few times to approve.
  • The final deliverable is a printed or emailed PDF schedule, so export quality matters as much as the app.

Solution

Projects break into rooms, and each room holds a grid of products with images, vendor, price, and approval status, mirroring how designers already think about the work.

Pasting a vendor URL starts a background import that extracts the name, images, price, dimensions, and specs. The card shows live status while it fills in, so the designer keeps working. When a site can't be read, manual entry is one click away.

Clients get a separate, minimal portal showing only their products, grouped by room, with approve and reject buttons and comments attached to each product.

  1. Designer

    • Workspace

      Rooms, products, schedules

  2. Import

    • Job queue

      One job per pasted URL

    • Extractor

      Name, images, price, specs

  3. Data

    • PostgreSQL

      Projects, rooms, products, approvals

  4. Client

    • Review portal

      Approve, reject, comment

    • PDF schedule

      One-click export

How a pasted vendor URL becomes a product a client can approve.
Product procurement view with a vendor URL import in progress
Client review portal with products grouped by room and approval buttons

Key decisions

  1. Imports run as background jobs

    Reading a vendor page can take several seconds and sometimes fails.

    Considered
    Scrape the page inside the request and make the designer wait.
    Chose
    Each pasted URL becomes a job with live status on the product card, with manual entry as the fallback.
    Trade-off
    A queue and job states to build and monitor, but the designer never stares at a spinner and one bad vendor page can't block the workspace.
  2. A separate portal for clients

    Clients open the tool rarely and only need to approve or question selections.

    Considered
    Give clients the designer workspace with restricted permissions.
    Chose
    A minimal review portal: their products, grouped by room, with two buttons and comments.
    Trade-off
    Two interfaces to maintain, but clients need no training and can't wander into the designer's tools.

Outcome

  • ~10 s

    From pasted vendor URL to a complete product card

    Typical import time for readable vendor pages.

  • Spec schedules export to a client-ready PDF in one click.

  • Client approvals happen on the product itself, and the designer is notified immediately.

Reflection

Scraping is never 100% reliable. Designing the manual-entry fallback as a first-class path, not an error state, mattered as much as the importer itself.