Studio Operations Platform
Replaced a photography company's entire spreadsheet-based operation with a three-portal web app.
- Next.js
- TypeScript
- React
- Tailwind
- shadcn/ui
- Supabase
- Postgres (RLS)
- Vercel
- Resend
- Dropbox API
- Slack API
- QuickBooks API
- Metricool API
- Claude Code
The client is a commercial real estate photography firm. I've left out its name, and every screenshot here uses seeded demo data.
Staff portal
Quality control
Social content
Contractor portal
Client portal
The problem
The whole company ran out of one Google Sheet: hundreds of projects, plus the schedule, editors, invoices, and client communication. Staff work from New York and the Philippines, and photographers and editors are contractors. Every hand-off was a cell someone had to remember to update.
What I built
One Postgres database behind three surfaces:
- Admin portal for staff (admins, sales, schedulers, producers, QC, editors, and social media managers), covering projects, scheduling, QC, delivery, invoicing, financials, and settings.
- Team portal for contractors, so photographers, videographers, and editors see only their own jobs and earnings.
- Client portal for brokers and asset managers: passwordless magic-link sign-in, live project status, deliverable previews, revision requests, and acceptance. It’s built and waiting on the client’s launch; clients aren’t on it yet.
It replaced the spreadsheet. Staff now run scheduling, QC, delivery, invoicing, and social content production through it, and I migrated about 1,000 historical records in.
The UI
There are a lot of roles and a lot of states, so I built a small design system and made it hard to drift from:
- A single tone registry for every status color, and a fixed type scale. Raw palette classes and arbitrary sizes are lint errors everywhere except the registry.
- Shared primitives (chips, callouts, steppers, empty states, tab navigation, deliverable galleries) reused across all three portals.
- Every date pinned to the studio’s time zone, also lint-enforced, because staff span two continents and the server renders in UTC.
- A staff guide with a page per role and per task, and screenshots generated by a script from seeded data so they never go stale.
Under the hood
- Security sits in Postgres row-level security with database-driven RBAC across staff, contractor, and client accounts. Server checks are backup, not the boundary. When the policies got slow, I audited them and fixed per-row permission checks across 175 policies.
- Integrations: Dropbox for deliverable storage, folder provisioning, and upload reconciliation; Slack for assignment DMs; QuickBooks Online for invoicing (built, waiting on the client to connect it); Resend for queued transactional email; and Metricool to publish social posts and pull back metrics.
- Background work runs on pg_cron and Vercel cron: dwell alerts, reminders, monthly social billing, and integration syncs.
How I built it
Solo, with an agent-first workflow. Parallel subagents handle implementation, code review, and client-feedback triage. A repo-level guide, custom lint rules, and per-domain docs keep agent-written code consistent with the rest of the codebase.
That added up to 300+ merged PRs in four months. Every merge deploys to production, and schema changes ship as migrations against live data, with 270+ migrations and no data lost.
I also ran the business side: discovery with the owner and staff, the statement of work and milestones, recurring software reviews with the client, and a paid scope expansion when the client wanted more.















