Appearance
Project Overview
This is a monorepo for Recruitee/Tellent - an Applicant Tracking System (ATS) and HR platform. The repository uses Nx as the build framework and contains multiple applications and shared libraries organized in a domain-driven structure.
Main Applications
- recruitee - ATS Dashboard for managing candidates, offers, and settings (domains/recruitee/apps/recruitee)
- recruitee-mobile - Mobile app built with Capacitor/Ionic (domains/recruitee/apps/recruitee-mobile)
- tellent-admin - Tellent admin interface (domains/tellent/apps/tellent-admin)
- tellent-client - Tellent client application (domains/tellent/apps/tellent-client)
- tellent-hr-mobile - Tellent mobile HR app (domains/tellent/apps/tellent-hr-mobile)
- account-setup - Onboarding portal for new users (domains/onboarding/apps/account-setup)
- viewbox - Sharing container/proxy for recruiters and candidates (domains/viewbox/apps/viewbox)
- referrals-hub - Referrals portal (domains/referrals/apps/referrals-hub)
- browser-extension - Chrome extension for sourcing candidates (domains/browser-extension/apps/browser-extension)
- integrationsui - Integrations UI (domains/integrations/apps/integrationsui)
Prerequisites
- Node 22.12.0
- pnpm 10.32.1
- Set NODE_OPTIONS:
export NODE_OPTIONS=--max_old_space_size=8192 - Install root certificates: https://github.com/Recruitee/dev-certificates?tab=readme-ov-file#install-ca-root
- Configure /etc/hosts with local domains (see README.md)
Common Development Commands
Starting Applications
bash
# Main recruitee app with staging API (most common for development)
pnpm start # or pnpm run serve:local-staging
pnpm run serve:local # Local app with local API
pnpm run serve:local-production # Local app with production-like config
# Other apps
pnpm run start:tellent-admin:local-staging
pnpm run start:tellent-admin:local
pnpm run start:tellent-client
pnpm run start:referrals-hub
pnpm run start:viewbox
pnpm run start:account-setup
pnpm run serve:integrations-ui:local-staging
# Mobile
pnpm run start:mobile # Start mobile dev server
pnpm run ios # Run on iOS simulator
pnpm run android # Run on Android emulator
pnpm run watch:ios # Run with live reload
pnpm run watch:android # Run with live reloadBuilding
bash
# Build specific app
npx nx build <app-name> -c <config>
# Common build configurations: local, local-staging, staging, production, ci
npx nx build recruitee -c staging
npx nx build tellent-admin -c productionTesting
bash
# Run unit tests for affected projects
npx nx affected -t test --base origin/master
# Run tests for specific project with Vitest
npx nx test <project-name>
# E2E tests (Playwright)
pnpm run start:recruitee-e2e:staging
pnpm run start:recruitee-e2e:local-staging
pnpm run start:recruitee-mobile-e2e:staging
pnpm run start:recruitee-mobile-e2e:local-staging
# E2E tests (Cypress)
pnpm run start:recruitee-e2e-cypress:staging
pnpm run start:recruitee-e2e-cypress:local-staging
npx nx e2e <app-name>-e2eLinting & Formatting
bash
# Lint affected projects
pnpm run lint # Lint only affected files
pnpm run lint:check:all # Lint everything
pnpm run lint:fix # Auto-fix lint issues
# Format code
pnpm run format # Format affected files
pnpm run format:all # Format all files
pnpm run format:check:all # Check formattingRunning Single Test
bash
# Vitest supports watch mode and test filtering
npx nx test <lib-name> --watch # Watch mode
npx nx test <lib-name> -t "test name pattern" # Run specific testRepository Architecture
Monorepo Structure
/domains # Domain-based organization
/{domain-name}
/apps # Applications for this domain
/libs # Domain-specific libraries
/subdomains # Sub-domain organization
/libs # Shared libraries across all domains
/{feature-name}
/data # NgRx stores, effects, API calls
/logic # Business logic, services
/smart-ui # Container components with state
/ui # Presentational components
/types # TypeScript types/interfaces
/util # Utility functions
/contracts # API contracts/interfaces
/legacy # Legacy code (being migrated)Library Organization Patterns
Libraries are organized by feature and responsibility:
- data - NgRx stores, effects, state management, API communication
- logic - Business logic, services, computations
- smart-ui - Container/smart components that connect to state
- ui - Presentational/dumb components (pure UI)
- types - TypeScript types, interfaces, enums
- util - Utility functions, helpers
- contracts - API contracts, DTOs
- legacy - Legacy code being migrated to new patterns
Import paths use @core/* aliases (e.g., @core/candidate-data, @core/candidate-ui). Check tsconfig.base.json for full mapping.
Build Configurations
Every application has at least 3 build configurations:
- ci - For feature branch builds (optimizations disabled)
- staging - For master branch builds, deployed to staging
- production - For releases, deployed to production
Builds output to dist/{app-name}/ with a webpack manifest.json.
State Management with Custom NgRx Library
We use a custom NgRx wrapper library (@recruitee/ngrx) that reduces boilerplate and provides:
Store Types
Single Entity Store - For single objects (e.g., configuration, current user)
typescriptconst { actions, selectors, reducer, namespace } = createSingleEntityStore<EntityType>()(key, options);Entity Collection Store - For CRUD operations on collections
typescriptconst { actions, selectors, reducer, namespace } = createEntityCollectionStore<EntityType>()(key, options);
Key Concepts
- Scopes - Filter/group entities (e.g., by status, filters)
- References - Automatically resolve relationships between entities
- Optimistic/Pessimistic Updates - Built-in support for both update strategies
- Pagination - Automatic pagination metadata handling
- Meta State - Tracks loading, pending, error states automatically
Creating Effects
typescript
class Effects {
public effect = createEntityEffectFactory(actions, this.actions$);
public fetchCollection$ = this.effect(
actions.fetchCollection,
action => this.api.fetch(action.payload)
);
}Effects automatically handle success/failure actions and state updates.
Data Modules
Keep data libraries as sub-features: @core/feature-name/data
Always import the data module into your app module to activate effects:
typescript
@NgModule({
imports: [FeatureDataModule],
})For detailed NgRx documentation, see docs/ngrx.md.
Angular & TypeScript Guidelines
Component Standards
- Always use standalone components (NgModules deprecated)
- Never explicitly set
standalone: true(implied by default) - Use signals for state management:
input(),output(),computed() - Set
changeDetection: ChangeDetectionStrategy.OnPush - Use native control flow:
@if,@for,@switch(not*ngIf,*ngFor,*ngSwitch) - Use class/style bindings instead of
ngClass/ngStyle - Prefer Reactive Forms over Template-driven forms
TypeScript
- Use modern Ecmascript features freely
- Use strict type checking
- IMPORTANT: Avoid
any- useunknownwhen type is uncertain - Prefer type inference when obvious
- Don't use lodash (see: https://youmightnotneed.com/lodash/)
Styling (CSS/Less)
- Use variables from
'theme/vars'for consistency - Use
:hostfor component scoping - Use
remfor font sizes (0.1rem = 1px),pxfor borders/paddings - Spacing variables in
libs/theme/util/theme/styles/utilities/spaces.less - Never hardcode colors - always use theme variables
- Avoid
::ng-deep- use Input props or CSS attributes instead - Use attribute-based utilities:
layout,layout-horizontal,layout-align-items-center,padding-tb-small-xx, etc.- Source:
libs/theme/util/theme/styles/utilities/layout.lessandspaces.less
- Source:
Naming Conventions
- Components:
{Name}Componentin{name}/{name}.component.ts - Services:
{Name}Servicein{name}.service.ts - Directives:
{Name}Directivein{name}.directive.ts - Pipes:
{Name}Pipein{name}.pipe.ts - Component selectors:
rt-{name}(namespace with rt-) - Pipe names: prefix with
rt(e.g.,rtDuration) - Directive inputs/outputs: prefix with directive name
Testing
Unit Testing with Vitest
- Test framework: Vitest (configured in workspace)
- Use
@ngneat/spectator/vitestfor component/service testing - Import from
@ngneat/spectator/vitest(not@ngneat/spectator) - Tests use
.spec.tsextension - See
docs/testing/using-spectator.mdfor Spectator patterns
E2E Testing with Playwright
- Primary E2E framework:
domains/recruitee/apps/recruitee-e2e(Playwright) - Cover new features with Playwright E2E tests when feasible
E2E Testing with Cypress (Legacy)
- Legacy Cypress E2E:
domains/recruitee/apps/recruitee-e2e-cypress - Uses harness pattern to avoid duplication
TPM (Tellent Project Manager)
TPM is a CLI tool for managing git sparse-checkout in this monorepo. It reduces disk usage and speeds up git operations by checking out only the domains/apps you need instead of the full repository.
Location: tools/tpm/
Setup
Add to ~/.bashrc or ~/.zshrc:
bash
tpm() { npx tsx ./tools/tpm/tpm.ts "$@"; }
eval "$(tpm completions)"Or run directly:
bash
npx tsx ./tools/tpm/tpm.ts <command>Commands
bash
tpm switch <spec...> # Set sparse checkout to specified domains/apps (replaces existing)
tpm add <spec...> # Add domains/apps to current sparse checkout
tpm remove <spec...> # Remove domains/apps from current sparse checkout
tpm list # Show all available vs checked-out domains/apps
tpm status # One-line status (useful for shell prompts)
tpm full # Disable sparse checkout, restore full working tree
tpm completions # Output shell completion script for bash/zsh
tpm help # Show usage instructionsSpecifier Syntax
recruitee # Entire domain (all apps, libs, subdomains)
recruitee/recruitee # Single app + domain's shared libs/subdomains
recruitee/recruitee-mobile # Another single appOptions
-y, --yes- Skip confirmation prompts-q, --quiet- Suppress informational messages
Key Details
- Core paths (
libs/,tools/,.ai/,scripts/, etc.) are always included regardless of selection - When specifying a single app, its domain's
libs/andsubdomains/are automatically included - Sparse checkout is local-only — CI pipelines and other developers are unaffected
- Requires Git >= 2.26 (cone-mode support)
Remote Cache
Speed up builds with GCS remote cache:
- Install gcloud: https://cloud.google.com/sdk/docs/install
- Login:
gcloud auth application-default login
Feature Deployments (Dynamic Stagings)
- Prefix branch:
feature/name-CU-<ticket-id> - Push to GitHub
- Wait for CircleCI build
- Access at:
https://dynamic.s.recruitee.com/name-CU-<task-number>/
Multiple branches with same marker override each other.
Dev Flags
Use dev flags to hide features before release or enable beta testing. Enable/disable from Superadmin. Clear flags after going live.
Design System & Storybook
User interface components are in libs/user-interface/*. Storybook serves as living documentation: https://recruitee.design/ (VPN required).
Use Component Driven Development: create/modify components in Storybook first, then integrate with data/logic.
Translations
- Add new translations to
ng2/i18n/locales/en.json - Never replace existing translations (unless not yet deployed)
- Change existing translations in Phraseapp or create new key
Important Files
eslint.config.cjs- ESLint configurationnx.json- Nx workspace configurationtsconfig.base.json- TypeScript paths and shared config.prettierrc- Prettier formatting rulespackage.json- NPM scripts and dependenciesdocs/- Additional documentation (style guide, NgRx, testing, FAQ)