Filtro para posiciones, encuesta para comprobar compatibilidad
mar, 7 mar 2023
Motivación
Aunque parezca mentira, debido a la alta demanda de desarrolladores de todo tipo en el mercado por parte de las empresas, muchos recruiters y headhunters se han vuelto máquinas del copy paste. No sé la cifra o la cantidad diaria de mensajes que envían a todos los perfiles de su red que hacen "match" con la posición nueva que tienen disponible, pero apostaría que no son pocos. Y no es que no me guste que me envíen mensajes, pero me gustaría que me enviaran mensajes que realmente me interesen y que me hagan pensar que han visto mi perfil, mi código o alguno de mis trabajos y que realmente me quieren contratar por mis habilidades.
No suele ser así, muchos usan programas para automatizar el proceso de búsqueda de perfiles como Hiretual o Jobvite, envían mensajes a todos los usuarios "que hacen match" y en ocasiones esto no funciona muy bien. Ya que en tu perfil pone claramente desarrollador Frontend, tu experiencia dice lo mismo... pero, ellos tienen una posición Java con microservicios increíble para ti.
Otras veces se mete la pata y te envían un mensaje con el nombre de tu compañero de equipo:
La idea
Cuantas veces has tenido una llamada con preguntas como:
- ¿ Por que buscas un cambio ?
- ¿ Conoces los principios SOLID ?
- ¿ Cuantos años dirías que has trabajo con React ? ¿ Y Typescript ?
- ¿ Sabes lo que es la integración continua ?
Para que despues te ofrezcan un salario inferior al que tienes, o que te pidan el CV para no volver a decirte nunca nada.
¿ How many times bro ?
La idea es filtrar tú antes que te filtren a ti.
Solución
10 simples preguntas sobre puntos que consideres importantes, requisitos, valores.. o básicamente lo que te de la gana y si se cumplen, el recruiter obtendrá información de contacto para poder entrar en detalle de todo el proceso.
Es un win win, ambos ahorramos tiempo, ¿ no ?
Código
Con un componente container o página, un par de botones, radiobuttons y un array de objectos tienes más que suficiente, en mi caso:
Tengo mi página:
import Dialog from '@/components/Dialog';
import styles from '@/styles/survey.module.css';
import ControlButtons from '@/components/ControlButtons';
import Confetti from 'react-confetti';
import SEO from '@/components/SEO';
import useWindowResize from '@/hooks/useWidowResize';
import useSurvey from '@/hooks/useSurvey';
import NavigationArrows from '@/components/NavigationArrows';
const { width, height } = useWindowResize();
<Confetti width={width} height={height} numberOfPieces={100} run={surveySuccess} />
title: 'Averigua si hago match con la posición en 1 minuto',
description: 'Click para continuar',
<div className={styles.header}>
{currentQuestionNum > 0 && currentQuestionNum < 11 && (
{currentQuestionNum} / {totalQuestions - 2}
// TODO: Move to a component
<div className={styles.container}>
(question: any, index: number) =>
currentQuestionNum === index && (
<div key={index} className={styles.question}>
dangerouslySetInnerHTML={{ __html: question.questionHtml }}
className={styles.questionHtml}
{question.answerOptions?.map((answerOption: any) => (
<label htmlFor={answerOption.answerText} key={answerOption.answerText}>
handleAnswerOptionClick({
question: question.questionText,
isCorrect: answerOption.isCorrect,
id={answerOption.answerText}
value={answerOption.answerText}
checked={answers[index]?.answer === answerOption.answerText}
{answerOption.answerText}
<div className={styles.buttons}>
hidden={currentQuestionNum < 1 || currentQuestionNum > 10}
disabledLeft={currentQuestionNum < 2}
disabledRight={questionsDoneNum < currentQuestionNum}
onClickLeft={handlePreviousQuestion}
onClickRight={handleNextQuestion}
Mi hook con lógica
import { useRouter } from 'next/router';
import { useReducer, useEffect, useRef } from 'react';
const reducer = (state: any, action: any) => {
...ya sabes como funciona un switch no?
const useSurvey = () => {
const { query } = useRouter();
const { name = '👋' } = query;
const emailRef = useRef<boolean>(false);
const [state, dispatch] = useReducer(reducer, initialState);
questionText: '¿ Que llegó antes el huevo o la gallina ?',
questionHtml: `<h2>¿ Que llegó antes el huevo o la gallina ?</h2>`,
answerOptions: [{ answerText: 'Huevo', isCorrect: true, }, { answerText: 'Gallina', isCorrect: false }],
if (state.currentQuestion === questions.length - 1 && !emailRef.current) {
await fetch('/api/email', {
'Content-Type': 'application/json',
subject: `Job survey from ${name} - ${new Date().toLocaleString()}`,
<code> ${navigator.userAgent} </code>
`<li> ${answer.question} : ${answer.answer} - ${
answer.isCorrect ? '✅' : '🚫'
}, [state, name, questions.length]);
const handlePreviousQuestion = () => {
if (state.currentQuestion > 1) dispatch({ type: 'PREVIOUS_QUESTION' });
const handleNextQuestion = () => {
if (state.currentQuestion != 0 && state.questionsDone >= state.currentQuestion)
dispatch({ type: 'NEXT_QUESTION' });
const handleAnswerOptionClick = (payload: {
}) => dispatch({ type: 'ADD_ANSWER', payload });
surveySuccess: state.success,
currentQuestionNum: state.currentQuestion,
questionsDoneNum: state.questionsDone,
totalQuestions: questions.length,
export default useSurvey;
Mi backend:
import type { NextApiRequest, NextApiResponse } from 'next';
import nodemailer from 'nodemailer';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const transporter = nodemailer.createTransport({
service: process.env.EMAIL_HOST,
pass: process.env.EMAIL_PASSWORD,
const { subject, message } = req.body;
await transporter.sendMail(mailOptions);
res.status(200).json({ success: true });
res.status(500).json({ success: error });
Resultado
Pruebalo tu mismo: Enlace al resultado