Skip to content

Directorio de talento

El directorio es el corazón operativo del reclutador HUMAE: un buscador potente de todos los candidatos con membresía activa, con filtros avanzados y vista detallada.

URL y acceso

URL: /recruiter/directorioEndpoint principal: GET /api/v1/directory/candidatesQuién puede usarlo: role:recruiter o role:admin.

Qué candidatos aparecen

Por defecto, el directorio muestra candidatos que cumplen todas estas condiciones:

  1. Membresía activamemberships.status = 'active' y expires_at > now().
  2. Estado visibleCandidateProfile.state ∈ {activo, en_proceso, presentado_empresa, entrevistado}.
  3. Perfil no soft-deleted.

Esto se puede modificar con filtros (un admin puede ver candidatos sin membresía con ?has_active_membership=0).

Filtros disponibles

El sidebar de filtros incluye (implementado en DirectorySearchService):

FiltroQuery paramTipoEjemplo
Búsqueda de textoqstring"product manager"
Estado del candidatostateenumactivo
Categoría (empleado/practicante)candidate_kindenumintern
País / Estado / Ciudadcountry_id, state_id, city_idFK32 (México)
Nivel de carreracareer_level_idFK5 (Senior)
Área funcional (legacy single)functional_area_idFK3 (Producción)
Áreas de interés (OR)functional_area_ids[]array FK[4, 7, 12]
Área principal del candidatoprimary_functional_area_idFK4
Posición específicaposition_idFK17 (Backend Developer)
Años de experienciayears_exp_min, years_exp_maxint3 / 10
Salario máximo ofertadosalary_maxnumber50000
Disponibilidadavailabilitystringinmediata
Abierto a remotoopen_to_remotebool1
Abierto a reubicarseopen_to_relocationbool1
Skills (AND)skills[]array FK[1, 5, 12]
Idiomas (AND)languages[]array FK[1, 2]
Membresía activahas_active_membershipbool1 (default)

Categoría (empleado / practicante)

Tres botones en el sidebar:

Categoría
[ Todos ] [ Empleado ] [ Practicante ]

Al elegir "Practicante" se envía candidate_kind=intern. Los candidatos con candidate_kind = null no pasan este filtro (el campo es estricto). En las cards, los practicantes muestran un badge ámbar; los empleados, badge color marca.

Áreas de interés (OR semántico)

A diferencia de skills/languages que usan AND, las áreas de interés usan OR: si pasas functional_area_ids[]=4&functional_area_ids[]=7, aparecen candidatos que tengan alguna de esas dos áreas en su perfil. Implementación:

php
$query->whereHas('functionalAreas', fn (Builder $q) =>
    $q->whereIn('functional_areas.id', $areaIds));

Si el reclutador necesita un candidato cuya área principal sea una específica (no secundaria), usa primary_functional_area_id:

php
$query->whereHas('functionalAreas', fn (Builder $q) =>
    $q->where('functional_areas.id', $primaryId)
      ->where('candidate_functional_areas.is_primary', true));

Semántica AND en skills/languages

Si pasas skills[]=1&skills[]=5&skills[]=12, solo aparecen candidatos con las 3 skills. Implementación en DirectorySearchService:

php
foreach ($skillIds as $skillId) {
    $query->whereHas('skills', fn ($q) => $q->where('skills.id', $skillId));
}

Salario esperado vs máximo que la empresa paga

El filtro salary_max busca candidatos cuyo expected_salary_min sea menor o igual a salary_max. Si el candidato no puso salario esperado, también aparece (da beneficio de la duda).

php
$query->where(function ($q) use ($max) {
    $q->whereNull('expected_salary_min')
      ->orWhere('expected_salary_min', '<=', $max);
});

Búsqueda de texto

q busca en first_name, last_name, headline, summary con LIKE %term%. No hace full-text search (fase 2 con Scout + Algolia/Meilisearch).

Paginación

  • Default: 20 por página.
  • Configurable con per_page (máximo 50 por seguridad).
  • Respuesta incluye meta con total, current_page, last_page.

Listado (vista de tarjetas)

Cada candidato se muestra como DirectoryCandidateCard:

┌──────────────────────────────────────────────┐
│  [Foto]  Ana Pérez                           │
│          Backend Engineer Senior             │
│          [Empleado]  8 años exp.             │
│          CDMX · $50K-$70K                    │
│                                              │
│  🏷️ React  Node.js  PostgreSQL  AWS          │
│  🌐 Español (nativo)  Inglés (C1)            │
│                                              │
│  [Ver perfil]  [Añadir a favoritos ⭐]       │
└──────────────────────────────────────────────┘

El badge [Empleado] o [Practicante] aparece junto a los años de experiencia. Si el candidato no marcó categoría, el badge se omite.

Vista detallada

URL: /recruiter/directorio/{id}Endpoint: GET /api/v1/directory/candidates/{id}

Trae el candidato con todas sus relaciones cargadas (con eager loading explícito para evitar N+1):

  • Datos básicos + ubicación
  • Experiencias (cronológicas)
  • Educación
  • Cursos + certificaciones
  • Referencias (con contacto)
  • Categoría (empleado/practicante) y áreas de interés con la principal destacada con ⭐
  • Skills con nivel
  • Idiomas con nivel MCER
  • Documentos adjuntos
  • Psicométricos completados con scores
  • Asignaciones activas e históricas
  • Notas internas del recruiter

Acciones disponibles en el detalle

BotónEfecto
Añadir a favoritosPOST /recruiter/favorites con candidate_profile_id
Asignar a vacanteAbre modal con selector de vacantes activas → POST /vacancies/{id}/assignments
Descargar CVGET /recruiter/candidates/{id}/cv.pdf
Añadir nota internaCampo libre, solo visible a recruiters + admin
Enviar mensaje (Fase 2)WhatsApp o email

Ordenamiento

Por defecto: ORDER BY updated_at DESC (los perfiles más recientemente actualizados primero).

En fase 2:

  • Ranking por match contra una vacante específica.
  • Ordenar por psicométricos (score global).
  • Ordenar por última vez que aparecieron en pipeline.

Performance

  • Eager loading de user, skills, languages evita N+1.
  • Índices DB en state, (country_id, state_id, city_id), functional_area_id, career_level_id soportan los filtros típicos.
  • Para búsquedas con muchos filtros, la query tarda 30–80 ms en dev (SQLite) y se espera similar en prod con MySQL + índices apropiados.

Privacidad

  • El candidato NO sabe cuántas veces fue visto en el directorio.
  • El candidato NO sabe quién descargó su CV o quién lo añadió a favoritos.
  • Empresas cliente no acceden al directorio directamente — solo ven candidatos que HUMAE les presenta en su panel.

Reportes relacionados

Un admin puede consultar los candidatos más buscados en reportes:

  • Endpoint: GET /admin/reports/most-searched-profiles?limit=20
  • Métrica: candidatos con más aperturas de detalle por parte de recruiters en los últimos 30 días.
  • Útil para ajustar la curaduría y evitar sesgos.

Caso de uso: buscar un practicante de Sistemas

"Una empresa pidió un practicante de Sistemas para apoyar al área de TI. ¿A quién tengo?"

  1. Sidebar → Categoría: Practicante.
  2. Sidebar → Áreas de interés → buscar y marcar Sistemas.
  3. Confirmar "Solo con membresía activa" (default).
  4. La lista muestra a María Torres con badge ámbar de Practicante y chip ⭐ Sistemas. Click para abrir detalle.
  5. Desde el detalle: Asignar a vacante → seleccionar la vacante "Practicante de Sistemas". Movido al pipeline en columna sourced.

Si después una empresa solicita un Auxiliar de Almacén · Empleado:

  1. Sidebar → Categoría: Empleado.
  2. Áreas: Almacén + Logística (multi-select OR — aparece quien tenga cualquiera).
  3. Sofía aparece con su área principal Logística (estrella) y secundaria Almacén.

Siguiente

Ya con candidatos identificados, el recruiter los mueve al pipeline kanban para gestionar su progreso. Pipeline kanban →

Manual de usuario HUMAE · Uso interno