BioHubNet Training Platform · Design craft

The parts that don't screenshot.

Most portfolios show the happy path on a nice background. This is the other eighty percent — the end-to-end flows, the permission models, and what a product does at 2am when everything goes wrong.

Role
Product design & front-end — flows, system design, UI
Surface
Five products, one codebase, one identity
Scale
300+ trainees across 11 universities
Focus
Interaction design · permissions · recovery
01 — End-to-end interaction design

One grant, four actors, seven states.

EQUIP is BioHubNet's funding programme: micro-grants for Canadian biotech founders. Designing it meant designing a relay — a founder who has never logged in hands off to a reviewer, who hands off to a committee, who hands off to a public record. Every handoff is a place the flow can break, so every handoff got a designed failure state, not just a happy one.

Applicant · no account

Window opens

A public page shows the live deadline in Toronto time, derived from the canonical round schedule — never hand-typed.

EdgeClosed early? The window carries the reason the admin wrote, so nobody guesses.
Applicant

Apply

One form, two streams — VentureConnect (monthly) and VentureLift (rounds), each with its own cap and cadence.

GateSubmits are refused server-side after the deadline, not just hidden in the UI.
Reviewer · committee

Screen

Reviewers see a queue scoped to their committee — not an admin console with everything in it.

AuthWrong committee redirects home rather than teasing a page you can't open.
Committee

Decide

Decisions are recorded with who and when, so the audit trail is a by-product of the flow, not extra admin.

UndoReversible decisions get inline undo; irreversible ones get a confirm.
Admin

Track

Funded companies roll into a recipient dossier — one curated dataset behind every view.

DriftThe public list and the internal report render from the same source, so they can't disagree.
Partner · no account

Share out

An unlisted link opens the report with no login, for a partner who will never have an account.

RevokeExpired reads as a soft message; revoked reads as 404. Different on purpose.

The last two columns are the ones that matter. A flow that only works for people with accounts isn't finished — most of the humans in this programme never log in once.

02 — System maps

Five products that have to feel like one.

The platform isn't an app, it's a suite: learning, placement, funding, hiring, and the admin surface behind all of it. The design problem is that a trainee, an employer, and a grant reviewer all live in the same product and must never feel like they wandered into someone else's tool.

Map A — Surface architecture Five products · one identity · one design system
Product surfaces
Engage
Courses, pathways, certification ladders
Experience
Placements, talent matching, mock interview
Equip
Grants, deadlines, review, recipients
Employer
Postings, pipeline, team & hiring
Admin
Ops, workspace, reporting, settings
All five resolve through
One identity model
A single role ladder plus contextual membership — no per-product login
One design system
Shared primitives, a documented voice, one confirmation hierarchy
One data spine
Postgres + Prisma; every surface reads the same source of truth
Map B — The deadline reconciler Why a date is never typed twice

A funding deadline appears in four places: the public window, the applicant's submit gate, the admin calendar, and the reviewer's queue. Typing it four times guarantees they drift. So the schedule is declared once and everything else is derived from it.

Source
Canonical schedule

Published round dates live in one file — the single place a human edits.

Derive
Generated specs

Each round expands into the windows it implies, in Toronto time.

Reconcile
Sync, don't duplicate

Stale auto-rows are removed, missing ones inserted, edited ones left alone.

Enforce
One gate

The same computed instant closes the form and greys the button.

This started as a bug: a “last Monday of the month” rule silently disagreed with the published dates for three months of the year. The fix wasn't a better rule — it was deleting the rule and making the published dates the source.

03 — Permissions as a design problem

Three ways to say “you may.”

Most products pick one permission model and bend everything to fit. This one genuinely needed three, because the humans are different: staff who belong to an org chart, reviewers who belong to a committee, and partners who belong to nothing at all and still need to open a document.

Model 01

The ladder

A ranked role chain — trainee, employer, instructor, admin, superadmin. Higher ranks inherit everything below. Right for staff surfaces where seniority genuinely nests.

requireRole("admin")
Model 02

The membership

Rank breaks down for a grant committee: a reviewer isn't “more senior,” they're assigned. So access is membership-or-role — you're in the committee, or you're an admin.

requireCommitteeOrAdmin([…], […])
Model 03

The capability

For people with no account, the URL is the credential. A 128-bit token grants exactly one document, carries its own edit flag and expiry, and dies when revoked.

/share/<token>
CapabilityLadder (role)Membership (committee)Token (link)
Read a grant queueAdmin+Assigned reviewer
Decide an applicationAdmin+Assigned reviewer
Read the recipient reportAdmin+Anyone with the link
Mint / revoke a share linkAdmin onlyNever self-issuing
Edit a shared contact listAdminOnly if the link says so
Write through the agent APIToken + explicit env flag
The rule underneath

Every one of these fails closed. No token configured means writes are refused, not allowed. A missing committee redirects rather than rendering an empty shell. And a capability link can only ever be issued by someone higher up the ladder — a token can never mint another token. Permission design is mostly deciding what happens when the answer is “I don't know.”

04 — Recovery states

Design for the worst day, not the demo.

The states below are the ones users only meet when something has already gone wrong — a dead link, a failed save, a half-filled form about to go to a real partner. They're the least glamorous screens in the product and the ones that decide whether it feels trustworthy.

Recovery 01

Confirmation, tiered to reversibility

One confirmation pattern for everything is either exhausting or dangerous. Friction is matched to how hard the action is to undo.

UndoReversible — do it instantly, offer a way back.
ConfirmCostly but recoverable — one deliberate click.
Type itIrreversible — type the name to prove you meant it.
Type “Cohort 1” to delete 
Recovery 02

Never claim a save that didn't happen

Optimistic UI is a lie unless it can take itself back. If the request fails, the interface reverts and says so — instead of showing a confident “Saved”.

Dr. Amara Osei — Vireo Bio
Marked reached · reverting…
Couldn't mark Dr. Osei — reverted. Check your connection and try again.
Recovery 03

Stop the send before it embarrasses you

A campaign template with unfilled fields produces “closes .” in a real partner's inbox. The gap is caught while it's still fixable.

Email details
These fill the template's placeholders.
Still blank — these will show as gaps in the sent email: Deadline, Link. Fill them before you copy or email anyone.
Recovery 04

Disabled, with the reason attached

A dead control that won't say why is a bug report waiting to happen. The blocker and the fix travel with the button.

Priya Raghunathan
No email on file
“No email on file — add one before marking this contact emailed.”
Recovery 05

Expired and revoked are different words

A link that timed out is a person who needs a fresh one. A link that was revoked shouldn't confirm it ever existed. Same dead end, deliberately different doors.

This link has expired.
Ask whoever shared it to send you a fresh link.
Revoked links return a plain 404 — no hint, no history.
Recovery 06

Empty is a state, not an accident

Every list gets a composed zero-state that says what belongs here and how to put the first thing in it.

No external links yet
Create one to share this report outside the platform — no login needed for whoever opens it.
What I take from it

Trust is built in the states nobody demos.

01

Derive, don't duplicate

Every value a human types twice will eventually disagree with itself. Declare it once and compute the rest — most “data bugs” are really design decisions made too late.

02

Fail closed, explain loudly

When the system doesn't know whether you're allowed, the safe answer is no — but it owes you a reason. Silence is what makes software feel hostile.

03

Design for people without accounts

The most important reader of a document is often someone who will never sign up. If the flow ends at a login wall, the flow isn't done.