SPEC-152-Q4: Client-Facing Suitability Report Viewer
Capability: SUITABILITY-ENGINE · Status: draft Repo: suitability-engine
Audience: WTP engineers building/reviewing this; Origo’s own front-end team, who will build their own equivalent view against the JSON contract below (we do not ship this UI into their product — see Deployment model).
1. Why this exists
Section titled “1. Why this exists”Origo needs their end-client to view and acknowledge a suitability report,
matching the UX pattern already proven live in client-onboarding’s own
client-facing report (apps/client-onboarding/src/app/report/) — a
progressive-disclosure, section-by-section review that gates the
acceptance action on having actually viewed every required section, with
every transition captured as an audit event (the same pattern the real
client-portal repo formalises for FCA acknowledgement). This spec
documents that reference UX precisely enough for Origo to reproduce it (or
diverge deliberately), and ships a working reference implementation
proving the JSON contract is sufficient to drive it.
2. Reference UX (as built in client-onboarding, source of truth)
Section titled “2. Reference UX (as built in client-onboarding, source of truth)”2.1 Shell
Section titled “2.1 Shell”-
Fixed left sidebar (dark navy,
wtp-navy-deepin the source brand palette — a placeholder in the reference implementation, see Branding), full viewport height, collapsible on mobile behind a hamburger toggle. -
Report title + client name at the top of the sidebar (“Suitability Report” / “Jane Doe”).
-
Nine section links, each with an icon, in a fixed order:
Order Section id Label 1 overviewOverview 2 about-youAbout You 3 objectivesYour Objectives 4 riskAttitude to Risk 5 recommendationsMy Recommendations 6 suitabilityWhy This is Suitable 7 costsCosts & Charges 8 risksKey Risks 9 downloadsDownloads -
Each link shows a checkmark once viewed (teal accent), and the currently active section gets a left border + lighter background — pure client-side state, driven by navigation, not a separate “mark as read” button.
-
“Proceed to Acceptance” button, fixed at the bottom of the sidebar, disabled until every section in a required subset has been viewed (not necessarily all nine — client-onboarding’s
REQUIRED_SECTIONS_FOR_ACCEPTANCEis a subset; Origo should decide their own required set, but the pattern — some sections may be “must-read”, others optional context — is worth keeping).
2.2 Section-view tracking = the audit trail
Section titled “2.2 Section-view tracking = the audit trail”This is the part that matters most and is easy to under-build: viewing a
section is itself the event being audited, not a side effect of a “mark
read” click. In the reference implementation, navigating to a section
fires a “viewed” event exactly once per section per session (idempotent —
revisiting doesn’t re-fire). client-onboarding persists this server-side
per client (reportSectionsViewed on the client record, patched via the
API) so the gate on “Proceed to Acceptance” survives a page reload — this
is not just client-side UI state, it’s the FCA-relevant record of what the
client actually looked at before accepting.
For Origo: this means your own back end needs an equivalent durable “sections viewed” store per client engagement, and your acceptance/proceed action needs to check it server-side, not just trust client-side state — otherwise the audit trail is forgeable by anyone who can call your “accept” endpoint directly.
2.3 Section content pattern
Section titled “2.3 Section content pattern”Each section is its own scrollable panel in the main content area.
Representative example (suitability, the “Why This is Suitable” section):
- A heading card explaining the recommended management approach
(
recommendation.dfmManageretc.) with a 3-up feature-benefit grid. - A performance-comparison visual (current vs. recommended platform).
- A list of collapsible reason cards — each with a heading and an expandable explanation (accordion pattern, one open/closed state per card, independent of the others).
Other sections (About You, Objectives, Risk, Recommendations, Costs, Risks, Downloads) follow the same visual language — card-based panels, consistent spacing, no page-level scroll competing with section-level scroll.
2.4 Data mapping — SuitabilityData → the nine sections
Section titled “2.4 Data mapping — SuitabilityData → the nine sections”The reference implementation renders directly off GET /v1/tenants/:tenantId/clients/:clientToken/suitability (or the format=json
export route)‘s response, i.e. { data: SuitabilityData, audit } per
suitability-template/contract.ts. Mapping:
| Section | Source fields |
|---|---|
overview | client.fullName, report.generatedAt, firm.name, adviser.* |
about-you | client.* (address, DOB, risk profile, investment experience) |
objectives | client.objectives.timeHorizonYears |
risk | client.riskScore, client.riskRating, client.capacityForLoss |
recommendations | recommendedPlatform.*, recommendation.* |
suitability | recommendation.summaryStatement, recommendation.dfmManager |
costs | charges.*, currentPlatform.{amc,platformFee,totalCharges} |
risks | (see gap below) |
downloads | report.enclosures |
Known contract gap, not solved by this spec: client-onboarding’s real
suitability page renders a list of narrative reason cards
(suitabilityReasons: [{id, heading, explanation}]) that do not exist
anywhere in SuitabilityData (contract.ts). That narrative content is
generated/stored elsewhere in client-onboarding’s own client record, not
by this engine’s rules pipeline. The reference implementation below
synthesises a single generic reason card from recommendation.summaryStatement
instead of reproducing the real multi-card narrative — this is a
simplification, not full parity, and should be called out to Origo
explicitly rather than presented as done. Closing this gap for real means
either extending SuitabilityData with a suitabilityReasons array (a
schema change, rules-engine work) or accepting that the narrative
“why-suitable” copy is something Origo authors themselves per the derived
recommendation data. Same gap likely applies to risks (Key Risks) —
client-onboarding’s version isn’t traced to this contract either; the
reference implementation leaves it as an Origo-authored static section for
now.
2.5 Branding is NOT part of the contract
Section titled “2.5 Branding is NOT part of the contract”The colour palette, typography, and logo in client-onboarding’s real report are WTP/Benchmark brand tokens — not part of what Origo integrates against. The JSON contract carries data, never presentation. The reference implementation uses a neutral placeholder palette (documented in its own README) specifically so it reads as “swap this,” not “this is WTP’s brand, keep it.”
3. Integration flow — how Origo actually calls this
Section titled “3. Integration flow — how Origo actually calls this”- Origo’s back end calls
POST /v1/tenants/{tenantId}/clients/{clientToken}/suitability/export?format=json(bearer:SUITABILITY_ENGINE_SERVER_TOKEN, or the Origo-facing API-key-with-scopes model once that ships — seedocs/external/origo-suitability-and-adviser-api-design.md §5.1inclient-onboarding) to get{ data: SuitabilityData, audit }. - Origo’s front end renders it using their own equivalent of the shell described above (or the reference implementation as a starting point).
- Section-view events are recorded by Origo, in Origo’s own store — this engine has no concept of “has this client viewed this section”, and is not the right place to add one (it’s a stateless derive-and-render pipeline, not a session/audit-trail service).
- Origo’s own “accept” action is Origo’s own audit event, entirely outside this engine’s scope.
- If the branded document (not just the web view) is needed — e.g. for
download/email — that’s the
format=docxpath (suitability-engine#6, now implemented) against Origo’s own registered template.
4. Deployment model — what WTP ships vs. what Origo builds
Section titled “4. Deployment model — what WTP ships vs. what Origo builds”Per the scope decision made with the user 2026-08-16: WTP ships this
spec + a reference implementation (examples/client-report-viewer/) that
Origo can literally copy, fork, or just read for the interaction pattern.
WTP does not operate, host, or maintain a production version of this
UI for Origo — Origo’s own client-facing product is Origo’s to build and
brand. The reference implementation is explicitly not embedded into
client-onboarding or any other WTP product’s UX.
5. Reference implementation
Section titled “5. Reference implementation”See examples/client-report-viewer/ — a standalone Vite + React app (no
Next.js/WTP-app-shell coupling, so it’s genuinely portable). Run
npm run dev inside that directory; it renders against a bundled sample
SuitabilityData payload (examples/client-report-viewer/src/sample-data.ts)
matching the shape a real API response would carry, so no live backend
call is required to see it work. See that directory’s own README for
architecture notes and exactly what is/isn’t implemented.
