All projects
SaaS case study
Project Management Platform
Task, time and client-visibility tooling for a small agency running parallel projects.
- Status emails
- -73%
- On-time milestones
- +29%
- Client portal usage
- weekly
src/lib/pipeline.ts — VS Code
export async function run(job: Job) {
const key = idempotencyKey(job)
if (await seen(key)) return skip(job)
const result = await withRetry(() =>
provider.execute(job.payload), { max: 5 })
await persist(result)
return ok(result)
}Interface reference — rendered from the real component structure, not a stock image.
The problem
Clients asked for status updates constantly because there was nowhere to see progress.
The solution
Internal task board plus a read-only client portal that shows milestones without exposing internal notes.
Hard parts
Two audiences, one dataset. Field-level visibility rules keep internal discussion private.
Outcome
Status-update emails largely disappeared, and project scope stayed visible to everyone.
Technology used
- React
- TypeScript
- Supabase
- React Query
How it was delivered
- 01Client problemAccount managers spent hours writing update emails.
- 02ResearchAsked clients what they actually wanted to know — dates and blockers, nothing else.
- 03PlanningSplit internal and external views at the data layer, not the UI layer.
- 04WireframeBoard for the team, timeline for the client.
- 05DevelopmentRow level security per project membership, typed API surface.
- 06TestingVerified no internal field could leak into the portal payload.
- 07DeploymentRolled out to two pilot clients before the full book.
- 08ResultsClients self-serve status; the team writes fewer emails.
Lessons learned
Enforce visibility in the database, not the component. UI-level hiding is not security.