A clickable prototype of Project Types running the real app with no backend and no login. This document is the build spec: what each screen does, which rules it encodes, and exactly which parts are real UI versus mocked data. There is no branch and no PR to read — the prototype plus this page is the whole handoff.
https://project-types.thrive-ui-prototype.pages.dev
Sign in with your @thrivetrm.com Google account — the site sits behind Cloudflare Access and is not reachable otherwise.
It runs entirely in your browser against mock data. Nothing you do is saved, nothing reaches a real environment, and no analytics events are emitted. Click freely; a reload resets everything.
Every surface reads one canonical order, defined once in
src/core/types/ProjectType.ts. It is a reading order: the work with a client
and a clock on it first, then the type that behaves most like it, then the two cultivated
pools that are rarely urgent.
Client mandates with a fee and a contract.
A named executive's replacement plan.
A cultivated pool for a function or role family.
Vetted people kept on deck for future roles.
Pipelines, Succession Plans and Talent Pools share one
capability profile — no fee, no contract, no client mandate. Wherever a screen
behaves differently “on a Talent Pool, Pipeline or Succession Plan”, it is
that one set being tested, not three separate branches
(POOL_LIKE_PROJECT_TYPES).
Each screenshot is the prototype as deployed. The components underneath are the real ones, edited in place — that edit is the design spec.
The index shows exactly one project type at a time, chosen from the header picker. That single-select rule is what makes the rest of the index coherent: stage positions are only comparable inside one type's stage set, so “highest stage reached” can only be filtered or sorted once the list is narrowed to one type. Per-type columns, per-type status options and the incumbent filter all follow from the same premise.
Each project type edits its own stage set, so the tab renders one section per type rather than one table. Editing one does not affect the others.
A third view alongside kanban and list. Rows are grouped into collapsible stage buckets, so the stage set stays legible as a spine while the row data becomes scannable and sortable in a way the cards never were.
These are invariants, not pixels. They are the part most likely to be lost in translation, so they are stated explicitly.
PROJECT_TYPE_ORDER is read
by the index picker, the Hub's sections, the person panel's sub-tabs, and the create
and edit forms. There were four separate orderings at one point, each individually
justified — which is exactly how a product ends up naming the same four things in
four sequences. Do not add a second one. The admin Stages page is the single documented
exception, and it says so in a comment.project_type_id is NOT NULL and
existing rows backfill to Search, so the card, global search and both project
autocompletes always show a type, falling back to Search rather than omitting the chip.
The filters had already chosen that fallback, and display disagreeing with filtering is
worse than either rule alone. A project rendering no type is a data defect to raise,
not a state to design for.// src/core/types/ProjectType.ts
export const PROJECT_TYPE_ORDER: readonly TProjectTypeTag[] = [
'search',
'succession_plan',
'pipeline',
'talent_pool',
];
export const POOL_LIKE_PROJECT_TYPES: readonly TProjectTypeTag[] = [
'pipeline',
'succession_plan',
'talent_pool',
];
export const DEFAULT_PROJECT_TYPE_TAG: TProjectTypeTag = 'search';
/** The incumbent is the only field that genuinely separates the pool-like types. */
export function hasIncumbent(tag: TProjectTypeTag): boolean {
return tag === 'succession_plan';
}
export function defaultProjectTypeForTenant(isVcPeCustomer: boolean): TProjectTypeTag {
return isVcPeCustomer ? 'pipeline' : 'search';
}
// admin Stages: a section reads its config instead of branching on the tag inline.
// src/legacy/pages/admin/customization/project-workflow/components/projectTypesData.ts
export type TProjectTypeConfig = {
tag: TProjectTypeTag;
showOffLimits: boolean; // Search only: presupposes an engagement
showHideCandidatesToggle: boolean; // Search only: presupposes a client who can see it
};
// Section order here is ALPHABETICAL — the one exception to PROJECT_TYPE_ORDER.
export const PROJECT_TYPES: readonly TProjectTypeConfig[] = [
{ tag: 'pipeline', showOffLimits: false, showHideCandidatesToggle: false },
{ tag: 'search', showOffLimits: true, showHideCandidatesToggle: true },
{ tag: 'succession_plan', showOffLimits: false, showHideCandidatesToggle: false },
{ tag: 'talent_pool', showOffLimits: false, showHideCandidatesToggle: false },
];
The prototype runs the actual application bundle. Everything you see rendered is the real component tree; everything it asks the network for is intercepted.
| Layer | Status | What that means for you |
|---|---|---|
| Components, layout, styling | real | Real files in src/legacy/ and src/app/, edited in
place. Spacing, states and responsive behaviour are what the app will do. |
| Interactions & local state | real | Drag and drop, sorting, filtering, tab and view switching, column reordering, form validation — all real client-side logic. |
| Routing | real | The real router. One caveat below on deep links. |
| Every network response | mocked | Intercepted by MSW and served from typed fixtures in
src/prototype/. That directory is scaffolding — ignore it when
building. Fixtures are typed against the real types in
src/core/types/, which is what stands in for a backend. |
| Persistence | mocked | Nothing survives a reload except the localStorage preferences (chosen project type, board/table view, column set). Creating or editing a project appears to work and is then gone. |
| The project-type taxonomy | mocked | The four types, their labels, descriptions and per-type stage sets are fixture data. The rules in section 04 are the spec; the specific stage names are illustrative. |
| Permissions & feature flags | mocked | One fixture user, an Administrator on an Executive Search Firm tenant. Role gating is not exercised. |
| Telemetry | off by design | PostHog, Pendo and Intercom keys are blanked; New Relic no-ops. A prototype session emits nothing. |
Artefacts of the mock layer, not intended design. Flagged so nobody builds them.
/people/<id>/associations straight into the address bar
lands on Page Not Found. Open the record first, then click the tab. Not a design
change — an unhandled route in the prototype.employments/grouped_by_company, educations), task counts,
custom report templates, and outreach sources. Unrelated to Project Types.These are the files the prototype actually changed. There is no diff to read, so this list is the map.
| Surface | Files |
|---|---|
| Canonical rules | src/core/types/ProjectType.ts |
| Admin stages | admin/customization/project-workflow/components/ProjectTypeStagesSection.tsx,
projectTypesData.ts, tabs/StagesPage.tsx,
ProjectStagesTable.tsx |
| Create project | jobs/index/JobCreateQvp.tsx,
jobs/index/ProjectTypeSelect.tsx |
| Projects index | jobs/index/useProjectTypeView.ts,
JobFilterPopover.tsx, JobsPage.tsx,
JobListView.tsx, data-grid/columnUtils.ts,
store/ui/indexPageSlice.ts |
| Hub | hub/components/ProjectTypeSection.tsx,
hub/components/JobList.tsx |
| Project panel | jobs/profile/PageHeader.tsx,
routing/panel/jobs/job/tabs/useGetJobTabs.ts,
jobs/profile/qvp/JobOverviewInfoEdit.tsx,
jobs/profile/JobEditOverviewForm.tsx |
| Candidate table (new) | jobs/profile/manage-candidacies/table/ — CandidateTableView.tsx,
candidacyColumnDefs.tsx, CandidacyColumnPicker.tsx,
StageBucketLoader.tsx, rows.ts,
sortCandidacies.ts, cells/ |
| Person panel | components/shared/associations/associationTabs.ts,
routing/panel/people/person/tabs/associations/loaders.ts |
| Search & autocompletes | GlobalSearchAutocompleteOption.tsx,
option-renderer/renderProjectOption.tsx,
forms/FormRelatedToAutocomplete.tsx,
shared/projects/cards/ProjectCardHeader.tsx |
| Prototype scaffolding (ignore) | src/prototype/, .env.prototype,
the *:proto package scripts |
2026-08-20_product-brief_project-types.md2026-08-20_solution-design_project-types.md2026-08-20_decision-memo_project-types.md2026-08-25_jira-epic_project-types.md2026-08-25_visual-brief_project-types.html