# Recruiting Module The Recruiting module (ATS — Applicant Tracking System) provides a full hiring pipeline management experience. It covers job offer creation and publishing, candidate pipeline tracking, and applicant screening. ## Screens | Screen | Route | Description | |---|---|---| | **Offers list** | `/recruiting/offers` | Paginated list of all job offers with search and sorting | | **New offer** | `/recruiting/offers/new` | Multi-step wizard to create a new job offer | | **Job detail** | `/recruiting/offers/:id/detail` | Read-only summary of all sections of a job offer | | **Edit job offer** | `/recruiting/offers/:id/edit` | Edit the General Config section of an existing job offer | | **Offer detail** | `/recruiting/offers/:id` | Candidates table for a job, with pipeline stage filters | | **Publish offer** | `/recruiting/offers/:id/publish` | Publish or manage job postings on external job boards | | **Applicant detail** | `/recruiting/offers/:id/applicants/:applicantId` | Full applicant screening view with stage progression, files, and notes | ## Top-level structure ``` Recruiting/ ├── index.tsx # React Router setup (lazy-loaded routes + permission guards) ├── routes.ts # Centralized route helpers (recruitingRoutes) ├── services.ts # All API calls (Axios, /ats/admin base URL) ├── types.ts # All shared TypeScript types and enums ├── queries.ts # React Query key factories (jobKeys, applicantsKeys) ├── constants.ts # Module-level configuration ├── utils.ts # Shared helper functions ├── dateUtils.ts # Date formatting utilities │ ├── utils/ │ └── form.ts # requiredLabel / optionalLabel helpers for form field labels │ ├── hooks/ │ └── useUploadFile.ts # Generic file upload hook — accepts uploadService as param │ ├── Offers/ # Job offer management — see Offers/README.md ├── Applicants/ # Cross-offer applicants list (shell — not yet fully active) └── components/ # Shared UI components used across the module ``` ## Key patterns ### State management All server state is managed via **React Query** (`react-query`). Query keys are defined in `queries.ts` using factory functions (e.g. `jobKeys.detail(id)`). Mutations invalidate the relevant query keys on success. ### API layer All API calls live in `services.ts`. The base URL is `/ats/admin`. Functions follow the naming convention `verb + Resource` (e.g. `createJob`, `getJobDetailApplicants`, `deleteApplicationStageNote`). ### Routing Routes are defined as nested React Router objects in `index.tsx`. All routes are lazy-loaded via `lazyRetry` and wrapped with `PermissionsGuard` using `ROUTE_PERMISSIONS.ATS`. Route path helpers are centralised in `routes.ts` via the `recruitingRoutes` object. ### i18n All user-facing strings use the `useLokaliseTranslation` hook. The primary namespace is `ats`. ### Theming Pages that require the HuGo design system theme wrap their content with `useHuGoTheme()` and `useHideHeader()`. ## Permissions The entire module is gated behind `ROUTE_PERMISSIONS.ATS`. Individual screens do not add further permission checks beyond that guard. ## Notable shared components | Component | Location | Purpose | |---|---|---| | `SideStepper` | `components/SideStepper` | Step navigation sidebar used in the create wizard | | `Footer` | `components/Footer` | Next / Back / Draft action bar for the wizard | | `ModalUnsavedChanges` | `components/ModalUnsavedChanges` | Prompt before navigating away with unsaved changes | | `StatusPill` | `components/StatusPill` | Job offer status badge | | `JobsTable` | `components/JobsTable` | Infinite-scroll table used on the offers list | | `ApplicantPersonalInfoForm` | `components/ApplicantPersonalInfoForm` | Candidate personal info form used when adding/editing a candidate. Uses `watch` + `trigger` to force async validation after phone autocomplete | | `JobDetailsForm` | `components/JobDetailsForm` | Source + pipeline stage selector used in add/edit candidate drawers | | `TitleWithCopyTooltip` | `components/TitleWithCopyTooltip` | Title with a copy-to-clipboard icon that appears on hover without layout shift | | `CandidatesTableEmptyState` | `components/CandidatesTableEmptyState` | Empty state for candidates tables | | `TableError` | `components/TableError` | Error state for tables | | `TableNoInternetConnection` | `components/TableNoInternetConnection` | Offline state for tables | | `TableSkeleton` | `components/TableSkeleton` | Loading skeleton for tables | | `DetailSectionSummary` | `Offers/JobDetail/components/DetailSectionSummary` | Read-only card for key/value section data, supports nested subsections |