Skip to content

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

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 reload

Building

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 production

Testing

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>-e2e

Linting & 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 formatting

Running 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 test

Repository 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

  1. Single Entity Store - For single objects (e.g., configuration, current user)

    typescript
    const { actions, selectors, reducer, namespace } =
      createSingleEntityStore<EntityType>()(key, options);
  2. Entity Collection Store - For CRUD operations on collections

    typescript
    const { 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 - use unknown when 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 :host for component scoping
  • Use rem for font sizes (0.1rem = 1px), px for 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.less and spaces.less

Naming Conventions

  • Components: {Name}Component in {name}/{name}.component.ts
  • Services: {Name}Service in {name}.service.ts
  • Directives: {Name}Directive in {name}.directive.ts
  • Pipes: {Name}Pipe in {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/vitest for component/service testing
  • Import from @ngneat/spectator/vitest (not @ngneat/spectator)
  • Tests use .spec.ts extension
  • See docs/testing/using-spectator.md for 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 instructions

Specifier Syntax

recruitee                    # Entire domain (all apps, libs, subdomains)
recruitee/recruitee          # Single app + domain's shared libs/subdomains
recruitee/recruitee-mobile   # Another single app

Options

  • -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/ and subdomains/ 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:

  1. Install gcloud: https://cloud.google.com/sdk/docs/install
  2. Login: gcloud auth application-default login

Feature Deployments (Dynamic Stagings)

  1. Prefix branch: feature/name-CU-<ticket-id>
  2. Push to GitHub
  3. Wait for CircleCI build
  4. 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 configuration
  • nx.json - Nx workspace configuration
  • tsconfig.base.json - TypeScript paths and shared config
  • .prettierrc - Prettier formatting rules
  • package.json - NPM scripts and dependencies
  • docs/ - Additional documentation (style guide, NgRx, testing, FAQ)