Skip to content

Instantly share code, notes, and snippets.

@lucasKoyama
Last active October 17, 2023 15:08
Show Gist options
  • Save lucasKoyama/01352443259ff20d7a1dd83859b0c20f to your computer and use it in GitHub Desktop.
Save lucasKoyama/01352443259ff20d7a1dd83859b0c20f to your computer and use it in GitHub Desktop.
Componente react / next para facilitar a vida de recrutadores ao usar seu portfólio!

Componente para ajudar os recrutaadores a ter uma melhor experiência de usuário ao navegador pelo seu portfólio!


Passo a passo para estruturar os arquivos

  1. Crie a pasta context com os seguintes arquivos dentro:
    • HrContext.jsx
    • HrProvider.jsx
  2. Crie a pasta hrTool com os seguintes arquivos dentro:
    • HrTool.jsx
    • hrTool.module.css

Propriedades do componente hrTool:

Coluna 1 Coluna 2
skills array de strings, contendo todas suas habilidades
personName string, contendo seu nome para uma possível integração com o backend para enviar a descrição da vaga para você caso queira

Estados fornecidos pelo context API

Coluna 1 Coluna 2
requirementsFilter array de habilidades que você possuí que bateu com a descrição da vaga, pode ser usado para resalta-las no seu portfólio
saveRequirements função que seta o estado dos requirementsFilter

Em breve terá um NPM desse componente, por hora vou deixa-lo como gist

import { createContext } from 'react';
const HrContext = createContext();
export default HrContext;
'use client'
import { useMemo, useState } from 'react';
import HrContext from './HrContext';
export default function HrProvider({ children }) {
const [requirementsFilter, setRequirementsFilter] = useState([]);
const saveRequirements = (requirementsArray) => setRequirementsFilter(requirementsArray);
const contextValue = useMemo(() => (
{ requirementsFilter, saveRequirements }
), [requirementsFilter]);
return (
<HrContext.Provider value={contextValue}>
{children}
</HrContext.Provider>
);
}
import { useContext, useState } from 'react';
import styles from './hrTool.module.css';
import HrContext from '../../context/hrToolContext/HrContext';
function HrTool({ skills, personName }) {
const [matchedSkills, setMatchedSkills] = useState([]);
const [requirements, setRequirements] = useState("");
const { saveRequirements, requirementsFilter } = useContext(HrContext);
// Function to calculate Levenshtein distance between two strings
const levenshteinDistance = (skill, word) => {
const skillChars = skill.length;
const wordChars = word.length;
const dp = new Array(skillChars + 1).fill(null).map(() => new Array(wordChars + 1).fill(0));
for (let i = 0; i <= skillChars; i++) dp[i][0] = i;
for (let j = 0; j <= wordChars; j++) dp[0][j] = j;
for (let i = 1; i <= skillChars; i++) {
for (let j = 1; j <= wordChars; j++) {
const cost = skill[i - 1] === word[j - 1] ? 0 : 1;
dp[i][j] = Math.min(
dp[i - 1][j] + 1,
dp[i][j - 1] + 1,
dp[i - 1][j - 1] + cost
);
}
}
return dp[skillChars][wordChars];
}
// Function to check if a skill is similar to a keyword
const isSimilar = (skill, keyword, maxDistance) => {
return levenshteinDistance(skill, keyword) <= maxDistance;
}
// Function to search for skills in a given input string
const findSkills = (input, skills, maxDistance) => {
const inputWords = input.split(/\s+/);
const matchingSkills = [];
for (const skill of skills) {
for (const inputWord of inputWords) {
if (isSimilar(inputWord.toLowerCase(), skill.toLowerCase(), maxDistance)) {
matchingSkills.push(skill);
break;
}
}
}
return matchingSkills;
}
const handleChange = (event) => {
setRequirements(event.target.value);
setMatchedSkills(findSkills(event.target.value, skills, 2));
}
return (
<section className={styles.section}>
<h3>Tem alguma descrição de projeto ou vaga?<br />Coloque-a abaixo e veja o quanto meu perfil bate com os requisitos!</h3>
<section className={styles.analysis_section}>
<section className={styles.input_area}>
<textarea
placeholder={`Exemplo:\n\nEstamos em busca de um desenvolvedor front-end habilidoso e criativo, com experiência em HTML, CSS, JavaScript, React, e responsividade.\n\nVocê será responsável por traduzir designs em interfaces interativas e otimizar o desempenho do usuário.\n\nTrabalhe conosco para criar experiências web inovadoras e cativantes. Se você é um entusiasta de tecnologia e está pronto para se destacar no desenvolvimento front-end, inscreva-se agora!`}
value={requirements}
onChange={(event) => handleChange(event)}
className={styles.blink}
/>
<label id="checkbox" className={styles.notification}>
<input id="checkbox" type="checkbox" />
{`Notificar ${personName} dos requisitos`}
</label>
</section>
<section className={styles.results}>
<h3>Habilidades identificadas que possuo</h3>
{
matchedSkills.length > 0 && (
<>
<ul>
{ matchedSkills.map((skill) => <li key={ skill }>{ skill }</li>) }
</ul>
<button onClick={() => saveRequirements(matchedSkills)}>
Destacar no portfólio habilidades identificadas
</button>
{
requirementsFilter.length > 0 && (
<p className={`animate__animated animate__fadeInDown ${styles.requirements_saved_message}`}>
As próximas seções do portfólio (Projetos e Tecnologias) foram destacadas de acordo com os requisitos!
</p>
)
}
</>
)
}
</section>
</section>
</section>
);
}
export default HrTool;
.section {
margin-top: -15vw;
position: relative;
}
label.notification {
display: flex;
flex-direction: row;
cursor: pointer;
margin: 0 auto;
}
label.notification input {
margin: auto 0.2rem;
box-shadow: none;
font-size: 2vw;
cursor: pointer;
}
.section label {
width: max-content;
font-size: 1em;
}
.section textarea {
height: 10em;
width: 100%;
resize: none;
background-color: #ebebeb;
margin: 0.5em 0;
text-align: justify;
font-size: 1em;
border: 1px solid #0997319e;
}
.section textarea:focus {
box-shadow: 0 0 0 0.2rem #0fcc454b;
animation: none;
}
.analysis_section {
width: 96%;
margin: auto;
}
.results {
font-size: 1.1em;
margin: 0.3em auto;
width: 100%;
}
.results ul {
width: 100%;
padding-top: 0.25em;
display: flex;
flex-wrap: wrap;
}
.results ul li {
width: 33.33%;
text-align: center;
}
.results button {
display: flex;
margin: 0.25em auto;
max-width: max-content;
padding: 0.5em 1.5em;
font-size: 1.25em;
border-radius: 10px;
cursor: pointer;
background-color: #57D67C;
border: none;
font-weight: 600;
color: white;
text-shadow: 0 0 4px #000000c2;
transition: all ease-in-out .3s;
}
.blink {
box-shadow: 0 0 0 0.01rem #0fcc45;
animation: blink 1s linear infinite;
}
.requirements_saved_message {
margin: auto;
padding: 0.5em 1em;
border-radius: 10px;
text-align: justify;
font-weight: 400;
color: #13752a;
background-color: #c9f0d2;
border-color: #0d6f24;
text-wrap: wrap;
}
@keyframes blink {
100% {
box-shadow: 0 0 0 0.4rem #0fcc4500;
}
}
@media screen and (min-width: 1024px) {
.section {
margin-top: -3vw;
}
.analysis_section {
width: 80%;
}
.results {
width: max-content;
}
.results ul li {
width: 25%;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment