talent-search-recruitment-flow

This is the recruiter-facing heart of youteacher_web: an employer or recruiter searches teacher profiles, spends quota to unlock a teacher's gated details, and then reaches out by contact or job invitation. The whole feature is built on the app's hexagonal layering — pure use cases, a gateway port, and concrete API adapters wired in by a React hook.

The shape of it

The feature separates what happens from how it talks to the backend:

  • Use cases (application layer) each hold one operation and one dependency. SearchTalentsUseCase takes filters, a page, a limit, and an optional AbortSignal, then delegates to the gateway. UnlockTalentUseCase, FetchViewerQuotaUseCase, FetchTalentDetailUseCase, SendContactUseCase, and SendInviteUseCase follow the same one-method pattern.
  • The port TalentSearchGateway is the contract the use cases depend on. It declares search, fetchDetail, sendContact, sendInvite, unlock, and fetchUnlocked. A separate ViewerGateway declares fetchQuota. The concrete implementations live in the infrastructure layer and are the only thing that knows about HTTP.
  • Dependency injection is a hook, not a framework. useTalentSearchService memoizes the concrete API gateways and constructs each use case around them, exposing a stable useCases object. Swapping the adapter never touches a use case.

Searching

useTalentSearch holds the page's state: the results list, pagination, and the search form (free-text query, availability, skill, location, and a relevance/recent sort). buildFilters collects only the fields the user actually set into a filters object.

Search runs only when the viewer is authorized. handleSearch resets to page 1, runs the use case, stores the results and total page count, and — on a wide desktop viewport — auto-selects the first result so the detail pane isn't empty. handleLoadMore fetches the next page and appends. A rate-limit response is caught specifically and surfaced as a friendly toast rather than a raw error.

Unlocking, quota, and reaching out

A teacher's contact details are gated. UnlockTalentUseCase takes a talent id and the viewer type (employer or recruiter); by its contract, the first unlock spends one unit of quota and later calls for the same teacher are idempotent — you are not charged twice. FetchViewerQuotaUseCase reads the viewer's remaining quota, and the gateway's fetchUnlocked returns which teachers are already open, so the UI can show unlocked state without re-spending.

Once unlocked, two outbound actions exist: sendContact carries a message to a teacher, and sendInvite attaches a job (id, and optionally title and a message) to the outreach. The TalentDetailPane renders the selected teacher — avatar, name, headline, availability, bio, skills, preferred locations, years of experience, and verifiable credentials — and hands its unlocked / contacted / invited state to a TalentActions block that drives the unlock, contact, and invite buttons per viewer type.

Selecting a teacher re-fetches fresh detail; a gone profile is dropped from the list, and a forbidden one clears the pane — the feature treats "no longer visible" as a normal state, not a crash.

Inside this node

  • part of ddd-hexagonal-architecture — the layering this feature is built on
  • the profile-service side of the same gate lives in youteacher_profile (talent unlock & quota enforcement)
about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →