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.
- Lead contributor · frontend
- Sep 2026 — Present
- Small frontend team; I authored about half the commits.
- Web · public site
- 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
- Public pages
- Public-only auth pages
- Protected member pages
- auth
- industries
- organization
- feedback
- member
- home
- Design system (shared/ui)
- API client
- Zod validation
- Stores
- TanStack Query — server data
- Zustand — auth & toasts
- Clientshot API
- 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
- Auth, directories, feedback and member features were spread across shared folders, so changes touched many places.
- Keep type-based folders (components, pages, services)
- One module per domain with a shared layer
- Moving code is disruptive while others are working; type-based folders scale badly as features grow.
- Move each domain into src/modules/* and shared UI, icons and assets into src/shared.
- A feature should be readable and changeable in one place.
- Auth, industries, organisations, feedback and the member area each live in one module.
02Put every API call behind a service boundary
- The UI had to be built before the backend existed, without baking the mock into components.
- Call fetch from components
- Module services that start as mocks and become real clients
- Direct calls are quicker at first; a service layer is extra structure that pays off at integration time.
- Each module talks to the backend only through its api/*.service.ts files over one shared client.
- Replacing the mock should change services, not screens.
- 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 71
- Largest contributor, from git history.
- Test files
- 88
- All authored by me.
- Public usage
- —
- 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.