LS / MTLLuke Sidney
Lighting the room0%
10th floor · Quartier Latin · golden hour
Montreal, QC·lukesidney.me·Fresh 2 · DenoNo cookies · No forms
Plate01

Front page.What it is

Live

PSA

A money cockpit for a service business: quote, contract, invoice, payment and hours in one app, in French first.

Professional services automation. My company's quotes run on it today. The screens below are the public demo; the company and every number in it are invented.

PSA demo overview: revenue, pending invoices, open quotes and recent activity for an invented renovation company.
Plate02

Set in type.The stack

What it runs on

One Next.js app on the shared database. The tax and PDF code live in shared packages the other apps use too.

Interface

  • Next.js16.2app and client portal
  • React19.2
  • Radix + shadcncustomized components
  • Recharts2.15charts

Money

  • Stripe20invoice payments
  • GST + QSTboth taxes on every document
  • PDF rendererquotes, contracts, invoices
  • ExcelJSexports

Data

  • Payload3.82records and admin
  • Neon Postgresone shared database
  • Drizzle0.45queries
  • Better Auth1.5sign-in

Mail + host

  • Handlebars + juiceemail templates
  • Resenddelivery
  • Vercelthe app and the demo
Plate03

On the press.How it works

5 steps, in order

From a blank quote to money in the bank. Every screen is the public demo.

  1. Build the quote

    Four steps. Line items sit under features, priced in hours times a rate. The PDF draws itself beside the form as you type.

    PSA quote builder, step 1 of 4, with the quote PDF drawing itself beside the form.
  2. Pass the send gate

    The quote cannot leave until it has a client, a project, a mandate, a payment method and a contract PDF.

    PSA quotes list with each quote's client, status and total.
  3. One link for the client

    They accept, sign the contract, fill in onboarding or ask for a revision behind one private link. Revision requests are sorted by AI.

  4. Get paid

    The invoice link opens a Stripe checkout. Payments land in Finances with GST and QST split out.

    PSA finances screen: revenue, taxes collected and charts by month.
  5. Clock the hours

    Hours from the day card land against the quote's features, so the estimate and the actual sit side by side.

    PSA time clock: the day card with work, travel and break entries.
Plate04

Pulled proof.The code

The send gate

apps/psa/lib/quotes/send-gate.ts · lines 35–45real source
export function evaluateSendGate(facts: SendGateFacts): SendGateResult { const missing: SendGateItem[] = [] if (!facts.hasClient) missing.push('client') if (!facts.projectName?.trim()) missing.push('project') if (facts.mandateCount < 1) missing.push('mandate') if (!facts.paymentMethod || !(PAYMENT_METHODS as readonly string[]).includes(facts.paymentMethod)) { missing.push('payment_method') } if (!facts.hasContractDraft) missing.push('contract') return { ok: missing.length === 0, missing }}

Both ways of sending a quote call this one function, so no screen can skip it.

Plate05

The reel.The walkthrough

Watch it run

Software demo coming soonAbout 2 minutes, on the demo: build a quote, watch the gate refuse it, attach the contract, send it, pay the invoice.
Plate06

Corrections.What it fixes

The problems it solves

  • Quotes built in a spreadsheet, outside any system.

    A four-step builder that draws the PDF as you type.

  • Contracts sent before anything was signed.

    Nothing leaves until the contract PDF is attached.

  • Unpaid invoices nobody sees until tax time.

    The overview opens on what is late and what is owed.

  • Fixed prices that never learn from real hours.

    Hours land on each feature, so the next quote starts from real numbers.

  • GST and QST worked out by hand.

    Both taxes are computed and split on every document.

Plate07

Editor's letter.Why I built it

Why it exists

PSA began as a quoting tool for construction materials, priced like “14 tonnes of gravel at $42 a tonne.”

When my company moved to software work, the quotes became hours. A fixed price without measured hours is a bet you renew with every mandate, so the app learned to track time against what it quoted.

Riso illustration: a quote form turning into a timesheet.
Plate08

Against the rack.Better than average

Against the usual tools

Against Harvest and FreshBooks. Their side was read from each vendor's own help pages on 2026-09-27.

WhatThe market averageThis app
Client stepsOne link to accept and e-sign; payment later, on a separate invoice.One link: accept, sign, onboard, pay.vendor's page
ContractA signature on the proposal (FreshBooks) or accept and decline only (Harvest).A contract PDF drawn from the quote; nothing is sent without it.vendor's page
Quebec taxesA tax rate you set up yourself.GST and QST built in, with presets for other industries.vendor's page
Plate09

The imprint.How long it took

152 commits in 293 days

commits
152
days with a commit
39
days, first commit to today
293
2025-12-09commits per week · busiest: 382026-09-27
  1. First commit
  2. Moved from materials to software mandates
  3. Moved into the monorepo
  4. Latest commit

Measured: git log, my own commits on the app, 2026-09-27.

Plate10

Colophon.Where it sits in the ledger

Remote▲ ▼ section · ◀ ▶ project · OK opens