Skip to content

Internal CRM & operations platform · 2026

StravoOS

StravoOS is the internal system I built to run Stravo — one application instead of a spreadsheet, a notes app and a calendar that never agreed with each other. It covers the whole cycle: a lead arrives, gets worked through a pipeline, becomes a client with projects and invoices, and every touch along the way is logged so the outreach numbers are derived rather than typed in.

19
Data models
11
Modules
~16,400
Lines of TypeScript
16
Validated action modules
StravoOS dashboard showing revenue, open pipeline value, outstanding invoices and new lead counts above panels for follow-ups due, today’s tasks and recent activity.
Dashboard. All figures shown are seeded demonstration data, not real business results.

Objective

What the problem was

  • Stop keeping the same lead in three places. A spreadsheet of prospects, a separate list of follow-ups and a notes file could not answer the only question that mattered — which deals are going cold right now.
  • Make the activity numbers trustworthy. Any figure I would act on had to be computed from logged events, not maintained by hand.
  • Build it so that adding a second user later would be a change, not a rewrite.

My role

Sole designer and developer. I made the product decisions — what the entities are, which modules earn their place, what degrades gracefully — and used AI assistance heavily while implementing them.

What I built

  • CRM with full lead records — status, source, priority, deal value, tags, objections, lost reasons — plus search, filtering, sorting, bulk actions and a per-lead activity timeline.
  • Drag-and-drop sales pipeline over the same lead records. Dropping a card on Won converts the lead into a client.
  • Outreach analytics derived entirely from logged activities: 30-day trend chart, pipeline funnel, reply and meeting conversion rates.
  • Tasks in board, list and calendar views, with priorities, due dates and recurring tasks that spawn their next occurrence on completion.
  • Clients module with projects, deliverables, invoices, documents and a two-way message thread.
  • A revocable, no-login client portal on a share token, giving a client a read-only view of progress and invoices and the ability to message back.
  • An automation engine with a visual trigger-and-steps builder and a public webhook, which qualifies an inbound lead, adds it to the CRM, creates a task, schedules a follow-up and logs a notification — with per-run step logs.
  • A website audit tool that scores a URL on SEO, speed, UX, trust and accessibility and exports a branded PDF report.
  • A streaming AI assistant with read access to the CRM, pipeline and outreach data, able to create tasks and notes.
  • An encrypted credential vault for client access details, and a ⌘K command palette for navigation and creation from anywhere.

Screens

Drag-and-drop sales pipeline board with columns for New Lead, Contacted, Interested, Meeting Booked and Proposal Sent, each card showing a company, contact and deal value.
The pipeline board and the CRM table are two views over one Lead record. Seeded demonstration data.
Cold outreach analytics screen with calls, emails, replies and meetings booked, a 30-day activity line chart and a pipeline funnel with conversion percentages.
Every number here is computed from the activity log rather than entered by hand. Seeded demonstration data.
Automation builder showing a website form webhook URL and a vertical sequence of steps: AI qualify lead, add to CRM, create task, schedule follow-up and notify.
The automation builder. A website form posts to the webhook and the flow runs server-side, logging each step.
Lead detail page showing contact details, deal value, source and tags alongside quick actions to log a call, email, reply, meeting or note, and a dated activity timeline.
Lead detail with its activity timeline. Seeded demonstration data.
Client portal page viewed through a share link, showing project progress, deliverables and invoices without requiring a login.
The client portal, reached through a revocable share link with no login. Seeded demonstration data.

Tools

Application

  • Next.js 15 (App Router)
  • React 19
  • TypeScript
  • Server Components
  • Server Actions

Data

  • PostgreSQL
  • Prisma 7
  • Zod validation
  • Supabase Auth

Interface

  • Tailwind CSS v4
  • Radix primitives
  • dnd-kit
  • Recharts
  • cmdk

Other

  • Vercel AI SDK
  • Node crypto (AES-256-GCM)
  • Headless Chromium PDF export
  • Vercel

Challenges

The decisions worth explaining

Three views, one entity

The CRM table, the pipeline board and the outreach tracker all describe the same prospects. Modelling them as three datasets would have meant three places to update and three chances to disagree. They are all views over a single Lead record, so moving a card on the board is the same write as changing the status in the table.

Numbers you can act on

Outreach statistics are computed from an Activity log — calls, emails, replies, meetings, status changes — rather than stored as counters. It costs more query work, but it means the figures cannot drift away from what actually happened, and the same log powers the per-lead timeline for free.

Designing for a second user before there is one

Every table carries a userId from the first migration. That single decision is what turns multi-user support and row-level security into an additive change instead of a schema rewrite — the kind of thing that is nearly free at the start and very expensive later.

Accepting untrusted URLs safely

The audit tool fetches whatever URL a user types, which is a server-side request forgery risk. Requests are guarded before they are made so the tool cannot be pointed at internal network addresses. Client credentials in the vault are encrypted with AES-256-GCM under a key that only ever lives in the environment.

Degrading instead of breaking

Every optional capability has a fallback. Without an AI key, lead qualification drops to a keyword heuristic and proposals fall back to templates. Without a vault key, the vault is simply unavailable. The application runs and is useful with nothing but a database connection string.

What I learned

  • Schema decisions are the expensive ones. Interface mistakes take an afternoon to fix; the choice to put userId on every table, or to store money as integer minor units, is the difference between an evening and a rewrite.
  • Feature count is not product quality. The modules that earn their place are the ones that remove a manual step — the pipeline board and the automation webhook get used; a module that only displays data does not.
  • AI assistance made me faster at writing code and no faster at deciding what to build. The architecture notes in the repository README are the part I would defend in an interview.
  • Writing the documentation exposed weak design. Twice I changed the schema because the explanation I was writing did not hold up.

Source code

The repository is private because it is a working internal system. I am happy to walk through the code and the schema in an interview, or to grant read access on request.

Next project

NewLab Systems

A seven-page bilingual site for a specialist diagnostics distributor, hand-written with no framework and no build step.

Read on