Next/Queue
Valere CRM · Design foundations

Next

Queue

Work on this site that is agreed but not started: ten flows of the app to add, in the order agreed on 7 October 2026, one flow that waits for the app, and the pages about the project. One item at a time, each in a session of its own. This page holds everything a session needs to take an item without re-reading the whole site: the rules, the mechanics, the demo data, and for each flow its screens one by one.

An item is done when its page is on the site, npm run demo:build has rewritten the navigation, the screens were checked in a browser, the row below says Done, and anything the flow shows that the app does not have is an item on App changes.

The flows were read from the app's routes (web/packages/crm/src/routes.ts) and pages on 2026-10-07; the labels quoted below are the app's own on that date. Already on the site as flows: Sign in, Reset password and the Hatch Farm walkthrough. The Not found page and the Illustrations cover the error and empty states, so those are not flows.

Before you start

The rules of this site and the mechanics of a flow. Everything here was settled while the first flows were built; a new flow follows it without asking again.

Flows to add

In the order to build them. The first five earn their place first: the daily routine, where records come from, the Partnerships features nothing on the site shows yet, how a colleague gets an account, and a meeting logged once. Each flow below lists its screens in order; a screen id is a suggestion, the data-step is the step the rail shows.

#FlowWho signs inScreensStatus
1Morning meeting, the daily routineAlex Reid (MD), then Dan Hughes6Queued
2A new lead from nothingBeth Morgan7–8Queued
3Partnerships, from a recommendation to an offerSophie Turner6–7Queued
4A colleague gets an accountDemo Admin, then the new user6Queued
5Log meetingLiam Carter5Queued
6Construction, a tender and the contractor's recordDan Hughes4–5Queued
7Who sees whatAll six9Queued
8Find a dealAny role7Queued
9Why a deal is redLiam Carter5Queued
10My account and the sessionAny role5Queued
11Closing a dealLand, Partnerships3Waits for the app

1. Morning meeting, the daily routine

What it proves: the board has no data of its own. Every row is a live slice of a deal; a change on the deal moves the row at once (FR-20, goal 3).

Screens

  1. mm-board (step 1) · /morning-meeting · Alex Reid, MD. The Primary view: the segmented Primary | Legals, tiles Overdue, Due this week, On track, No next action; the table Deal, Next deadline, Open actions, Health. Rows sorted by next deadline: Ashcombe (Contractor; Overdue; Construction "Appoint contractor · 3 Oct"; Stalled: no contact 15 days; 55), Church Farm (Legals), Kings Mead (Heads of Terms; Due this week), Station Road Yard (Buyer; On track), Elm Park (Instructed; On track), Saw Mill Close (Exchange / Completion; No next action). Ring: the Ashcombe name → mm-deal. Watch for: the overdue row at the top.
  2. mm-deal (step 2) · /opportunities/ashcombe · Dan Hughes, Construction. The Overview with the Next actions card: "Appoint contractor", Construction, due 3 Oct, overdue in red; the header's Stalled badge and 55. Ring: Complete on that action → mm-done.
  3. mm-done (step 2) · the same page · the action struck through with Done; the Health score up (no overdue action); the badge stays (no contact 15 days is another rule). Then: board__more "Back to the board" → mm-board-after.
  4. mm-board-after (step 3) · /morning-meeting · Alex Reid. Watch for: Ashcombe's row dropped to No next action and the tiles changed (Overdue 0, No next action 2); nothing was typed on the board. Ring: Legals in the segmented → mm-legals.
  5. mm-legals (step 4) · /morning-meeting?view=legals · tiles Legal blockers ("A critical task past its deadline"), Critical items open, Exchanged ("Awaiting completion"); rows Church Farm (Legals: exchange and completion countdowns, the Oldest outstanding item, the open critical tasks), Saw Mill Close and Elm Road (Exchange / Completion, awaiting completion). Ring: Church Farm → mm-church-legals.
  6. mm-church-legals (step 4) · /opportunities/church-farm/legals · the deal clock and the Legal progress table with Church Farm's own tasks. End: "Start again from the board".

Data

The seed's Ashcombe (Contractor stage, Kennet Agreed, three contractors) and Church Farm (Legals, the fully populated Opportunity: read its Legal tasks, dates and actions from valere-demo.ts rather than reusing Hatch Farm's). The other rows as the walkthrough's board draws them (morningBoard in tools/genHatchFarm.mjs).

Confirm in the code first

  • pages/morning-meeting/PrimaryBoard.tsx, LegalsBoard.tsx, board-parts.tsx: the exact columns, whether the deal name links to the Opportunity, how the Legals row shows the countdowns and the oldest outstanding item.
  • pages/lead-detail/CompleteActionButton.tsx: the button's label and where it sits in the Next actions card (the walkthrough never drew a completed action; actionRows has a done state ready).
  • The MD may complete an action: Activities and Next Actions are open to every Valere role (ADR-0028); if the story reads better with Dan, switch the signed-in user between screens as the walkthrough does.

2. A new lead from nothing

What it proves: where the records come from. The walkthrough starts from a seeded Lead; this flow makes one, with its landowner, and shows the three other New forms.

Screens

  1. nl-overview (step 1) · /leads · Beth Morgan, Back Office. The Leads Overview with its tiles (Total sites, Instructed, Appraised, Not contacted), filters (Region, Owner, Stage) and the six seeded Leads. Ring: New lead (the primary button by the title) → nl-empty.
  2. nl-empty (step 2) · /leads/new · the empty form. Cards: Details (Site name *, placeholder "For example Hatch Farm"; Address *, "Street, town, postcode"; Region, hint "Used by the Leads Overview's region filter"; Area (acres), "0.0"; Estimated units, "0"; Planning status, "Select"; Proposed use), Landowner (Landowner account *, or New landowner name *), Source and ownership (Lead source *, Relationship owner *, First contact date, Notes). Aside: What happens next; Required ("Fields marked * are required."). Ring: Create lead (read the button's label) → nl-errors.
  3. nl-errors (step 2) · sent empty: the banner "The lead was not created. Fix the fields below and try again." and the field errors "Enter the site name.", "Enter the address.", "Pick a landowner account.", "Select a lead source.", "Select a relationship owner." (also known: "Enter acres, up to 2 decimals.", "Enter the new landowner's name.", "The first contact cannot be in the future."). Ring: the Site name field → nl-filled.
  4. nl-filled (step 3) · filled: Site name "Barn Close", address "Barn Close, Arborfield, RG2", Region Berkshire, 6.2 acres, 40 units; Landowner: New landowner name "Arborfield Farms Ltd" (the branch that creates the Account on the way); Lead source and Relationship owner Beth Morgan; First contact date today; a note. Ring: Create lead → nl-lead.
  5. nl-lead (step 3) · /leads/barn-close · the new Lead page: New Lead, Days open 0, the facts from the form, the Landowner card naming Arborfield Farms Ltd, no activity, no action. Watch for: the lead is on the Leads Overview and the landowner on the Accounts Overview at once.
  6. nl-account (step 4) · /accounts/new · the New account form, empty: Account name * ("For example Hatch Farm Estate Ltd"), Account type *, Sub-type * (for Other), Status, Address *; What happens next: "The account opens on its own page, using the template for its type." "It appears on the Accounts Overview straight away."
  7. nl-contact (step 4) · /contacts/new · the New contact form: Person (First name *, Last name *, Job title "For example Head of Land"), Account (Account *, or New account name * with Account type * and Sub-type *), Contact details (Phone, Email); What happens next: "The contact opens on its own page." "It is listed on the Contacts Overview and on its Account's page straight away." "Log a first activity from a deal the contact works on."
  8. nl-opportunity (step 5) · /opportunities/new · "New opportunity. Create a deal for a site that is already instructed, without a lead." Details (Site name * "For example Barn Close", Address *, Units *, Planning status, Region, hint "Used by the Opportunities Overview's region filter"), Deal (Landowner account *, Deal type * with the hint "Decides whether the Contractors tab appears", Land value (£), Expected fee (£), Opportunity owner *, Instructed on); the aside "Already have a lead?". End: "Start again from the Leads Overview".

Data

Invented for the story: Barn Close and Arborfield Farms Ltd (no such names in the seed). The selects' options come from the label maps in the code; do not guess them.

Confirm in the code first

  • pages/NewLead.tsx, NewOpportunity.tsx, NewAccount.tsx, NewContact.tsx (POC-6.0a, b, d, e): the submit buttons' labels, the layout (form left, the aside cards right), the "What happens next" sentences of New lead and New opportunity, and the option labels (LEAD_SOURCE_LABELS, PLANNING_STATUS_LABELS, PROPOSED_USE_LABELS, the account types and sub-types).
  • Where the New lead button sits on the Leads Overview, and the Add account / Add contact buttons on the other Overviews (their labels are "Add account" and "Add contact").

3. Partnerships, from a recommendation to an offer

What it proves: the POC-6 Partnerships features (S-1 recommended buyers, the Interest and Offers registers, the Buyer Health score), which no page on this site shows yet.

Screens

  1. pa-recommended (step 1) · /opportunities/elm-park/buyers · Sophie Turner, Partnerships. Elm Park (Instructed, S106) with no buyers yet, and the Recommended buyers card: the Buyer / RP accounts scored against the site (the Buyer match: active regions, unit range, affordable homes), the Minimum score filter, an Add button per row. Ring: Add on the best match → pa-added.
  2. pa-added (step 1) · the buyer table now holds the account at Coverage; its recommendation row reads "Added at Coverage". Then: board__more "See it on the register" → pa-interest.
  3. pa-interest (step 2) · /registers/interest · the Interest Register: tiles Live interests, Opportunities covered, Buyers interested, Stale over 14 days; columns Opportunity, Buyer, Buyer stage, Registered, Last contact, Next step; the new row at the top and a Stale tag on an old one; the filters. Ring: the Elm Park row → pa-offer.
  4. pa-offer (step 3) · back on the Buyers tab: the buyer moved to Interested, then Offer received, with the row's edit form open: Offer (£), Quality ("Not rated"), Next step; "A figure can be changed but not cleared once set." Ring: Save → pa-offers.
  5. pa-offers (step 3) · /registers/offers · Offers Received: tiles Offers received, Under review, Accepted, Average vs GDV; columns Opportunity, Buyer and next step, Offer, vs GDV, Status, Quality; the new offer under review. Ring: the buyer's name → pa-account.
  6. pa-account (step 4) · /accounts/<the buyer> · the Account page with the Buyer health score card (its factors: reply dates, completed or closed agreed deals, offer against GDV; "Every factor is at full points." when nothing counts against it) and the Buying criteria card (active regions, unit range, affordable homes). End.

Data

Elm Park and the seed's Buyer / RP accounts with their criteria (read valere-demo.ts for each buyer's regions and unit range, so the recommendation order is the one the app would give). The offer figure is invented; pick one under the site's GDV if the seed has one.

Confirm in the code first

  • pages/opportunity-detail/RecommendedBuyers.tsx and web/shared/components/BuyerMatch.tsx: the columns of the recommendation table, how the score is shown, the Minimum score options.
  • pages/registers/InterestRegister.tsx, OffersRegister.tsx: the filters (register.hasFilter exists), what "Stale" and "vs GDV" read; where a Site's GDV lives (the Offers register needs it: "No offer on a site with a GDV recorded yet.").
  • web/shared/components/BuyerHealth.tsx, pages/account-detail/BuyingCriteriaCard.tsx: the factor names and points.
  • Where the registers sit in the menu: /registers redirects to the Interest Register; the walkthrough's shell has the Registers item since POC-UI-1 (the Pipeline group, as the app's AppShell.tsx lists it).

4. A colleague gets an account

What it proves: there is no sign-up; an admin creates each user with one Valere role, and the role decides the homepage and the edit rights (POC-5.7, ADR-0029).

Screens

  1. st-signin (step 1) · /auth/sign-in · the Sign in screen with admin.demo@example.com typed (reuse the Sign in flow's screen in the Live app layout). Ring: Sign in → st-executive.
  2. st-executive (step 1) · /dashboard · Demo Admin. The Executive dashboard, read only: the menu has one more item, Create staff user; every write is locked "Read only for Admin." Ring: Create staff user in the menu → st-form.
  3. st-form (step 2) · /staff/new · "Create staff user. Add a colleague who can sign in to Valere CRM." The card User details: Role ("Select a role": Back Office, Land, Partnerships, Construction, MD), Email, Password with the Password rules checklist (8 to 72 bytes, "At least one number"); the aside cards Roles (what each role does) and Who can do this ("Only an admin can create staff users. There is no public sign-up."). Ring: Create → st-taken.
  4. st-taken (step 2) · sent with land.demo@example.com: the banner "The user was not created. Fix the email below and try again." and the field's "A staff user with this email already exists". Ring: the Email field → st-created.
  5. st-created (step 3) · sent with priya.nair@example.com, role Land: the green bar "Staff user created. priya.nair@example.com"; the form is empty again for the next colleague. Then: board__more "Priya signs in" → st-priya-signin.
  6. st-priya-signin (step 4) · the Sign in screen with the new email, then st-priya-dashboard (step 4) · /dashboard · the Individual dashboard · Land, with the Land role card Overdue follow-ups. Watch for: a new user has no name until they set it in My account; the top bar and the greeting show what the code shows for an empty name. End.

Branch, from st-form's caption: a Valere role opening /staff/new gets the red bar "Only an admin can create staff users."

Data

The admin is in the seed (Demo Admin, admin.demo@example.com). Priya Nair is invented; the seed's Valere-side Key Contact is a different fictional person, so check the seed for name clashes before using one.

Confirm in the code first

  • pages/CreateStaffUser.tsx: the submit button's label, the Roles card's text, the checklist's rules, the password field's own error.
  • components/AppShell.tsx: the menu with the admin's extra item (icon i-staff exists on the site), and what the sidebar's user card shows when the profile has no name.
  • components/StaffOnly.tsx and session/edit-rights.ts: which controls the admin sees locked, with "Read only for Admin." (also on the Components page under Text field).

5. Log meeting

What it proves: a meeting is recorded once and appears on every related record, and its follow-up is a Next Action with an owning function (POC-6.0c, FR-12, FR-13).

Screens

  1. lm-lead (step 1) · /leads/mill-field · Liam Carter, Land. Mill Field (Qualified) with its timeline and tracker as the seed has them. Ring: Log meeting (find where the page offers it) → lm-form.
  2. lm-form (step 2) · /leads/mill-field/log-meeting · "Log meeting. Record a meeting once. It appears on every related record." Cards: Details (Activity type "Meeting", hint "A meeting is logged as an activity"; Related to, the site, hint "It appears on this timeline and on each attendee's records"; Date *; Start time *), Attendees (Add attendee *, the contacts picker), Notes (What was discussed *), Follow-up (Action *, Owning function * "Choose a function", Due *); the aside What happens next ("The follow-up becomes a Next Action with its owning function." "The deal's days since last contact reset."). Ring: Save → lm-errors.
  3. lm-errors (step 2) · sent empty: "The meeting was not saved. Fix the fields below and try again." with "Choose the date.", "Enter the start time.", "Add at least one attendee.", "Enter what was discussed.", "Enter the action.", "Choose the owning function.", "Choose the due date." (also known: "A meeting cannot be in the future."). Ring: Add attendee → lm-filled.
  4. lm-filled (step 3) · filled: today 10:00, the attendee Mill Field's landowner contact (from the seed), "Walked the boundary; the owner wants a decision before Christmas.", the follow-up "Send the appraisal request", Owning function Back Office, due in a week. Ring: Save → lm-lead-after.
  5. lm-lead-after (step 3) · the Lead page: the timeline's new Meeting entry "With <attendee>", the tracker's new action with the Back Office chip, Last contact today, Health's Recent contact at 0. Then: board__more "The same meeting on the contact" → the attendee's Contact page (lm-contact, step 4) with the entry under its activity. End.

Confirm in the code first

  • pages/LogMeeting.tsx: the entry points on the Lead and Opportunity pages, the Save button's label, how attendees are picked and listed, whether the Opportunity variant differs.
  • Mill Field's landowner and contact in valere-demo.ts.

6. Construction, a tender and the contractor's record

What it proves: Construction's own day, and that a contractor's bids live on its Account page (POC-6.4), the way a buyer's deals live on its.

Screens

  1. ct-dashboard (step 1) · /dashboard · Dan Hughes, Construction. The Individual dashboard · Construction with the role card Tenders in progress: Ashcombe, "Awaiting contractor". Ring: Ashcombe → ct-tenders.
  2. ct-tenders (step 2) · /opportunities/ashcombe/contractors · the seed's three tenders: Northgate Build £7,650,000 Tender received, Brackenridge Construction £7,800,000 Tender received, Fairfield Contracting Invited; £ per sq ft and Rank if Ashcombe has a floor area. Ring: Move to Shortlisted on Northgate → ct-appointed.
  3. ct-appointed (step 2) · Northgate at Appointed; the header's Move to Heads of Terms now live (Kennet is Agreed, Land terms agreed if the seed says so). Then: board__more "The contractor's own page" → ct-account.
  4. ct-account (step 3) · /accounts/northgate-build · the Contractor Account page: the Bid history card (its Bid outcome filter; one row per tender with the site, the price and the outcome; "No bids with this outcome." when the filter finds none), the deals panel, Contacts, Activity. End.

Confirm in the code first

  • pages/account-detail/contractor/BidHistoryCard.tsx and the folder beside it: the columns, the outcome values, the "No bids yet" title.
  • pages/dashboard/role-cards.tsx: what the Construction card lists and the "Awaiting contractor" pill.
  • Ashcombe's floor area, Land terms and tender due dates in the seed.

7. Who sees what

What it proves: the same data, six homepages, and on one deal what each role may change (FR-25, goal 2).

Screens

  1. ws-backoffice … ws-construction (step 1) · /dashboard · the four Individual dashboards: Beth (role card Leads awaiting contact or appraisal: New Lead "Not contacted", Appraisal Requested "Awaiting appraisal"), Liam (Overdue follow-ups), Sophie (Live pitches and offers, with the "No buyer coverage" pill on a deal with no live buyer), Dan (Tenders in progress, "Awaiting contractor"). Each with its own tiles and My actions.
  2. ws-md, ws-admin (step 2) · the Executive dashboard for Alex Reid and for Demo Admin (read only, the extra menu item).
  3. ws-church-land, ws-church-partnerships, ws-church-construction (step 3) · /opportunities/church-farm · the same Overview as Liam (Land terms his, the stage moves his), as Sophie (Land terms "Set by Land. Read only for Partnerships."; the Buyers tab hers) and as Dan (the Contractors tab his, everything else locked with its owner named). The ring moves from one locked control to the next.

Build it as a table of nine screens rather than a story; the rail groups them in three steps.

Confirm in the code first

  • pages/dashboard/role-cards.tsx: the four cards' rows and pills; IndividualDashboard.tsx: the eyebrow "Dashboard · <role>" and the greeting.
  • session/edit-rights.ts: the exact hint sentences per field and per move; SITE_EDIT_FIELD_FUNCTIONS for who owns what.
  • individualDashboard in the builder is written for Beth only; it needs the user, the role card and the figures as arguments.

8. Find a deal

What it proves: the Overviews filter and search live, the filters live in the address, and an empty result says why (FR-14, FR-16).

Screens

  1. fd-leads (step 1) · /leads · any role. Tiles Total sites 6, Instructed 0, Appraised 1, Not contacted 1; the filters Region, Owner, Stage ("Region: All" …); the table Site, Units, Stage, Last contact, Owner, Health, Next action with the six Leads; the pager "Showing 1–6 of 6 sites". Ring: the Stage filter → fd-stage.
  2. fd-stage (step 1) · /leads?stage=APPRAISED · one row, Brook End; the tiles count the filtered set. Ring: the Owner filter → fd-none.
  3. fd-none (step 2) · /leads?stage=APPRAISED&owner=<Sophie> · "No leads match these filters." with Clear filters. Ring: Clear filters → fd-cleared.
  4. fd-cleared (step 2) · back to six rows. Ring: Brook End → fd-lead (step 2) · its Lead page.
  5. fd-accounts (step 3) · /accounts · tiles Total accounts, Landowners, Buyers / RPs, Contractors, Other professionals; the type tabs; the Account name search; columns Account, Type, Contacts, Live deals, Health, Last contact. Ring: the search with "Greenfield" → fd-accounts-none · "No accounts match these filters."
  6. fd-contacts (step 3) · /contacts · "Every person at an account, with how to reach them."; the Account type filter and the Name or account search; columns Name, Job title, Account, Phone, Email, Last contact. End.

Confirm in the code first

  • pages/list-params.ts: the query parameter names (region, owner, stage, page, search, type) so the frames' addresses are right.
  • pages/LeadsOverview.tsx, AccountsOverview.tsx, ContactsOverview.tsx, OpportunitiesOverview.tsx: the Health column on Accounts (the Buyer Health score), the Add buttons, the pager's sentence.

9. Why a deal is red

What it proves: every score and stall flag is explainable and curable; the score runs on read, so the cure shows at once (FR-23, FR-24, goal 3).

Screens

  1. rd-overview (step 1) · /opportunities · Liam Carter. The Opportunities Overview (tiles Total opportunities, Active, Weighted pipeline fee; filters Region, Deal type, Status; columns Opportunity, Units, Deal type, Status, Fee, Prob., Health, Last activity) with one deal red and badged. Ring: its Health chip → rd-breakdown.
  2. rd-breakdown (step 1) · the breakdown open: each component with its points and the fact behind it, the stall rules fired. Ring: the deal's name → rd-deal.
  3. rd-deal (step 2) · the Opportunity Overview with the overdue actions and the old last contact. Ring: Log activity → rd-logged.
  4. rd-logged (step 2) · the call logged today: Recent contact back to 0 points off, the no-activity rule gone; the score up but still amber. Ring: Complete on the overdue actions → rd-green.
  5. rd-green (step 3) · the score green, no badge; then the Overview row again with the green chip. End.

Data

Pick the deal by computing the components from api/src/site/site-health.ts (Recent contact −25 from 14 days, Overdue actions −10 each up to 3, Stage duration −20 past the stage's limit, Next action −10, Legals −15 per critical task past deadline up to 2, only in Legals): a deal with no contact for 20 days and three overdue actions is at 45; with a stage over its limit, 25 and red. Ashcombe in the walkthrough is at 55 with "no contact 15 days"; give it two more overdue actions, or use a deal of your own in the story, and say in the caption what was assumed.

10. My account and the session

What it proves: a user keeps their own profile and password, and a password change ends the session on purpose (KAN-13, ADR-0012).

Screens

  1. ma-account (step 1) · /account · any role (Liam). "My account. Your profile and password." The Profile card: First name, Last name, Email, Role (the role read only), Save; the Change password card: Current password, New password (hint "Counted in bytes: some symbols count as more than one."), Confirm new password. Ring: Save on the profile → ma-saved · "Profile saved." in the green bar.
  2. ma-password-errors (step 2) · the password form sent wrong: "Enter your current password.", "The passwords do not match.", "The current password is incorrect." Ring: Change password → ma-changed.
  3. ma-changed (step 2) · "Password changed. Sign in with your new password." Watch for: the session ends on purpose (the refresh token could not be renewed later), so the next screen is Sign in. Then → ma-signin (step 3) · the Sign in screen.
  4. ma-signout (step 3) · the user block in the top bar is one link to My account; Sign out is on that page. The screen shows it ringed and the caption ends on Sign in. End.

Confirm in the code first

  • pages/Account.tsx: the buttons' labels, where Sign out sits, whether the email is editable.
  • The Sign in flow's "session ended" screen is Design only; this flow's return to Sign in is the app's real behaviour and shows the plain Sign in screen.

11. Closing a deal

Waits for the app. The stage table has a close to Closed / Lost with a reason (ADR-0017, moveSiteStage takes closedLostReason), but the pages do not offer it: pages/lead-detail/StageMoves.tsx (line 42 on 2026-10-07) keeps only the forward and back rows, row.kind !== 'CLOSE'. The Opportunity page shows "Closed / lost: <reason>" for a deal the seed closed (Hollow Lane, Orchard Rise), and a lost Lead opens on the Lead page.

Not flows, on purpose

Pages about the project

Listed in the menu as planned. On the same tokens and components, after the flows or between them.

PageWhat it holdsSourceStatus
About the projectWhat the Valere CRM PoC is, its ranked goals, what is deliberately out of scope.docs/BUSINESS_MODEL.mdPlanned
Roles and functionsThe five Valere roles and the admin, who owns which stage, locked fields.docs/DOMAIN_GLOSSARY.md, ADR-0019, ADR-0028, site-stage.config.tsPlanned
Deal lifecycleThe stages from New Lead to Exchange / Completion, the moves and their conditions.site-stage.config.ts, ADR-0017, ADR-0019; the Handoff line sign-in board draws itPlanned
App screensThe CRM pages: overviews, Lead, Opportunity with its tabs, the board, the dashboards.docs/ARCHITECTURE.md; the walkthrough's screens can be reused one per pagePlanned
Data modelSite, Company, Contact, the buyer and contractor links, the computed values.api/prisma/models/, docs/DOMAIN_GLOSSARY.md ("Computed on read")Planned