Back

2026

Agency CRM

Internal CRM for MiidayStudio · built by Vikrant Kumar

An internal CRM for a design-and-dev studio that turns inbox enquiries into a tracked pipeline, from lead to project to paid invoice.

ReactTypeScriptViteTailwind CSSMotionHonoCloudflare WorkersCloudflare D1
Live siteSource is private. Happy to walk through the code in an interview.

The problem

At MiidayStudio, project enquiries arrived as emails, deals were remembered rather than tracked, and invoices lived in separate documents. I wanted one internal tool for the three founders: leads, clients, projects, tasks and GST invoices, with enquiry emails turning into leads on their own. It also had to run entirely on Cloudflare, with no servers to look after.

Decisions and why

Email in, lead out

The studio's enquiries already land in an inbox, so the Worker also handles Cloudflare Email Routing. It parses 'Label: value' lines (name, organisation, capabilities, brief, repo) into a lead. The same parser backs a 'paste an email' box and a keyed webhook, so the website doesn't need to change.

Workers and D1 for both halves

A Hono API on a Worker with D1 (SQLite) for storage, and the React app served as Worker static assets. One platform, one bill, and deploys straight from GitHub. For a three-person team, a managed SQL database was plenty.

Winning a lead creates the client and project

Converting a lead creates the client (reusing one with the same email), opens a project with the brief and repo link carried over, and marks the deal won. Nobody retypes details between the pipeline and delivery.

Overdue is computed, not stored

A 'sent' invoice past its due date is reported as overdue when it is read, instead of by a scheduled job flipping a flag. There is no cron to fail, and the status can't go stale. Invoice totals and GST are worked out on the server, so the client can't submit its own total.

No seeded password; access-code sign-up

The first visit creates the founder account inside the app, so no default credentials ship in the repo. Teammates then join with a studio access code, or an admin adds them. Passwords are hashed with PBKDF2 through the Web Crypto API, which is what the Workers runtime offers.

The hardest part

Making first-run setup safe to fail. On the first deploy I hadn't set the signing secret, and account creation saved the founder to the database before trying to issue a login token. The token step crashed, which left a half-created admin that nobody had the password for and blocked setup from running again. I fixed it by checking the config before touching the database, so a missing secret now returns a clear error and writes nothing. I also added the access-code sign-up path and a safe way to reset the users table, then re-tested the flow against a real D1 database through wrangler.