Skip to content
Suleiman Francis

Clientshot Public · SeamHealth Group

Building the public side of a feedback platform: discovery, reviews and contributor rewards.

Public Clientshot site where people browse organisations by industry, write and track feedback, and earn points and badges React 19, strict TypeScript and 88 test files.

Role
Lead contributor · frontend
Timeline
Sep 2026 — Present
Team
Small frontend team; I authored about half the commits.
Platform
Web · public site
Stack
  • React 19
  • TypeScript
  • Vite
  • Tailwind CSS
  • React Router
  • TanStack Query
  • Zustand
  • React Hook Form
  • Zod
  • Vitest
  • Testing Library

Clientshot Public is a SeamHealth Group product. No screenshots, code or customer data are shown.

Context

Clientshot Public is the consumer side of Clientshot. People browse organisations by industry, read and write reviews, follow the resolution of feedback they’ve submitted, and earn points and badges for contributing.

My responsibility

I owned

  • The app shell and configuration, and the move to a modular, domain-based structure
  • Industries directory and organisation profiles: cards, filters, reviews and testimonials
  • The member area: my reviews with conversation threads, leaderboard, points and badges, notifications and settings
  • Auth: sign-in, sign-up, password reset and email verification
  • The four-step write-feedback flow
  • Integration with the backend API and real authentication
  • The test suite — all 88 test files
  • Shared UI: a self-contained data table and pagination, and normalised border and shadow tokens

I collaborated on

  • Other modules and fixes with the rest of the frontend team
  • Designs from Figma; tickets and review through the team’s process

The challenge

A public review site has to be easy to browse while signed out, safe to use while signed in, and faithful to a detailed design across many screens directories, profiles, a multi-step submission flow, a rewards system and account settings.

The first version also ran on a mock backend, so the architecture had to make the switch to the real API a contained change rather than a rewrite.

Constraints

Built ahead of the backend
Screens were built against a client-side mock before the API existed.
Design fidelity
Many screens with a detailed Figma spec, so shared components had to absorb the visual detail once.
Shared repository
Several engineers working in parallel, so structure and boundaries mattered.

Architecture

  1. Routes
    • Public pages
    • Public-only auth pages
    • Protected member pages
  2. Modules
    • auth
    • industries
    • organization
    • feedback
    • member
    • home
  3. Shared
    • Design system (shared/ui)
    • API client
    • Zod validation
    • Stores
  4. State
    • TanStack Query — server data
    • Zustand — auth & toasts
  5. Backend
    • Clientshot API
Simplified. Each module owns its pages, components and API services.
  • Each domain is a module with its own components, pages and api/*.service.ts files; shared/ui is the only design system.
  • Protected routes redirect to sign-in; sign-in pages redirect already-signed-in visitors to their reviews.

Key decisions

01Organise by domain module, not by file type

Problem
Auth, directories, feedback and member features were spread across shared folders, so changes touched many places.
Options
  1. Keep type-based folders (components, pages, services)
  2. One module per domain with a shared layer
Trade-offs
Moving code is disruptive while others are working; type-based folders scale badly as features grow.
Decision
Move each domain into src/modules/* and shared UI, icons and assets into src/shared.
Reason
A feature should be readable and changeable in one place.
Result
Auth, industries, organisations, feedback and the member area each live in one module.

02Put every API call behind a service boundary

Problem
The UI had to be built before the backend existed, without baking the mock into components.
Options
  1. Call fetch from components
  2. Module services that start as mocks and become real clients
Trade-offs
Direct calls are quicker at first; a service layer is extra structure that pays off at integration time.
Decision
Each module talks to the backend only through its api/*.service.ts files over one shared client.
Reason
Replacing the mock should change services, not screens.
Result
The switch to the real API and real authentication landed as a focused integration change.

Implementation

Write feedback
A four-step review submission modal.
Forms
React Hook Form with Zod schemas; a shared FormField handles labels, errors and accessibility wiring.
Routing
React Router with lazy-loaded routes, protected and public-only guards, and separate layouts for public, auth and member pages.
Testing
Vitest and Testing Library across components, forms and modules — 88 test files.

Production

Quality
Strict TypeScript, ESLint and Prettier, with type-checking as part of the build.
Hosting
Built and deployed through AWS Amplify.

Outcome

I built most of the public site and all of its tests, then connected it to the real backend and authentication.

Commits authored
36 of 71Measured
Largest contributor, from git history.
Test files
88Measured
All authored by me.
Public usage
—Not measured
Impact metric unavailable.

Lessons

Building against a mock is only cheap if the mock sits behind the same boundary the real API will use. Because it did, integration changed services rather than screens.

Next iteration

  • Contract tests between the service layer and the API, so backend changes are caught before they reach the UI.
  • Pre-render the public directory pages for search engines.