Cookies
This website uses cookies to improve the user experience, more information on the legal information path.
Notas
0
años
0
meses
0
días
0
horas
0
minus
0
segus
sáb16mar20:16
Esta es la imagen de fondo
3

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:

Coincidencia

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:

survey.tsx

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 Survey = () => {
const { width, height } = useWindowResize();
const {
questions,
answers,
surveySuccess,
totalQuestions,
currentQuestionNum,
questionsDoneNum,
handleNextQuestion,
handlePreviousQuestion,
handleAnswerOptionClick,
} = useSurvey();
return (
<>
<Confetti width={width} height={height} numberOfPieces={100} run={surveySuccess} />
<SEO
noimage={false}
meta={{
title: 'Averigua si hago match con la posición en 1 minuto',
description: 'Click para continuar',
noindex: true,
}}
/>
<Dialog
open
modalMode
withPadding
header={
<div className={styles.header}>
<ControlButtons />
<span>
{currentQuestionNum > 0 && currentQuestionNum < 11 && (
<span>
{currentQuestionNum} / {totalQuestions - 2}
</span>
)}
</span>
</div>
}
body={
// TODO: Move to a component
<div className={styles.container}>
{questions.map(
(question: any, index: number) =>
currentQuestionNum === index && (
<div key={index} className={styles.question}>
<div
dangerouslySetInnerHTML={{ __html: question.questionHtml }}
className={styles.questionHtml}
/>
{question.answerOptions?.map((answerOption: any) => (
<label htmlFor={answerOption.answerText} key={answerOption.answerText}>
<input
onChange={(e) =>
handleAnswerOptionClick({
question: question.questionText,
answer: e.target.value,
isCorrect: answerOption.isCorrect,
questionNum: index,
})
}
type="radio"
id={answerOption.answerText}
name={String(index)}
value={answerOption.answerText}
checked={answers[index]?.answer === answerOption.answerText}
/>
{answerOption.answerText}
</label>
))}
</div>
)
)}
</div>
}
footer={
<div className={styles.buttons}>
<NavigationArrows
hidden={currentQuestionNum < 1 || currentQuestionNum > 10}
disabledLeft={currentQuestionNum < 2}
disabledRight={questionsDoneNum < currentQuestionNum}
onClickLeft={handlePreviousQuestion}
onClickRight={handleNextQuestion}
/>
</div>
}
/>
</>
);
};
export default Survey;

Mi hook con lógica

useSurvey.ts

import { useRouter } from 'next/router';
import { useReducer, useEffect, useRef } from 'react';
const initialState = {
currentQuestion: 0,
questionsDone: 0,
success: false,
answers: [],
};
const reducer = (state: any, action: any) => {
...ya sabes como funciona un switch no?
};
const useSurvey = () => {
const { query } = useRouter();
const { name = '&#128075;' } = query;
const emailRef = useRef<boolean>(false);
const [state, dispatch] = useReducer(reducer, initialState);
// Prepotencia here!
const questions = [
{
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 }],
},
];
useEffect(() => {
if (state.currentQuestion === questions.length - 1 && !emailRef.current) {
(async () => {
try {
await fetch('/api/email', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
subject: `Job survey from ${name} - ${new Date().toLocaleString()}`,
message: `
<h1> ${name} </h1>
<code> ${navigator.userAgent} </code>
<ol>
${state.answers.map(
(answer: any) =>
`<li> ${answer.question} : ${answer.answer} - ${
answer.isCorrect ? '✅' : '🚫'
} </li>`
)}
</ol>
`.replace(/,/g, ''),
}),
});
emailRef.current = true;
} catch (e) {
console.log(e);
}
})();
}
}, [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: {
question: string;
answer: string;
isCorrect: boolean;
questionNum: number;
}) => dispatch({ type: 'ADD_ANSWER', payload });
return {
questions,
answers: state.answers,
surveySuccess: state.success,
currentQuestionNum: state.currentQuestion,
questionsDoneNum: state.questionsDone,
totalQuestions: questions.length,
handleAnswerOptionClick,
handleNextQuestion,
handlePreviousQuestion,
};
};
export default useSurvey;

Mi backend:

email.ts

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,
auth: {
user: process.env.EMAIL,
pass: process.env.EMAIL_PASSWORD,
},
});
const { subject, message } = req.body;
const mailOptions = {
from: process.env.EMAIL,
to: process.env.EMAIL,
subject: subject,
html: message,
};
try {
await transporter.sendMail(mailOptions);
res.status(200).json({ success: true });
} catch (error) {
res.status(500).json({ success: error });
}
}

Resultado

Caso de éxito Correo con las respuestas

Pruebalo tu mismo: Enlace al resultado