Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 122 additions & 108 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@eslint/js": "^9.33.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react": "^5.0.0",
"@vitejs/plugin-react": "^5.0.2",
"autoprefixer": "^10.4.21",
"eslint": "^9.33.0",
"eslint-plugin-react-hooks": "^5.2.0",
Expand Down
12 changes: 7 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Home } from "./pages/Home.js";
import Habilidades from "./components/Habilidades/Habilidades";
import Preview from "./components/Preview";

export default function App() {
return (
<>
<Home />
</>
)
<main className="p-6 grid grid-cols-2 gap-6">
<Habilidades />
<Preview />
</main>
);
}
76 changes: 76 additions & 0 deletions src/components/Habilidades/habilidades.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

import { useState } from "react";
import { useCurriculo } from "../../context/CurriculoContext";

export default function Habilidades() {
const { habilidades, adicionarHabilidade, removerHabilidade } = useCurriculo();

const [nome, setNome] = useState("");
const [nivel, setNivel] = useState("");

const handleAdicionar = () => {
if (nome.trim() === "" || nivel === "") {
alert("Preencha o nome e selecione o nível!");
return;
}

adicionarHabilidade({ nome, nivel });
setNome("");
setNivel("");
};

return (
<div className="p-4 border rounded bg-white shadow">
<h2 className="text-xl font-bold mb-4">Seção de Habilidades</h2>

{/* Formulário */}
<div className="flex gap-2 mb-4">
<input
type="text"
value={nome}
onChange={(e) => setNome(e.target.value)}
placeholder="Digite uma habilidade..."
className="border px-3 py-2 rounded w-full"
/>

<select
value={nivel}
onChange={(e) => setNivel(e.target.value)}
className="border px-3 py-2 rounded"
>
<option value="">Selecione nível</option>
<option value="Básico">Básico</option>
<option value="Intermediário">Intermediário</option>
<option value="Avançado">Avançado</option>
</select>

<button
onClick={handleAdicionar}
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
>
Adicionar
</button>
</div>

{/* Lista dinâmica (agora usando o contexto) */}
<ul className="space-y-2">
{habilidades.map((item, index) => (
<li
key={index}
className="flex justify-between items-center bg-gray-100 p-2 rounded"
>
<span>
{item.nome} — <strong>{item.nivel}</strong>
</span>
<button
onClick={() => removerHabilidade(index)}
className="text-red-500 hover:text-red-700"
>
Remover
</button>
</li>
))}
</ul>
</div>
);
}
40 changes: 40 additions & 0 deletions src/components/Preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

import { useCurriculo } from "../context/CurriculoContext";

export default function Preview() {
const { personalData, habilidades } = useCurriculo();

return (
<div className="p-6 border rounded bg-gray-50 shadow">
<h2 className="text-2xl font-bold mb-4">Preview do Currículo</h2>

{/* ----------------- DADOS PESSOAIS ----------------- */}
<div className="mb-6">
<h3 className="text-lg font-semibold">Dados Pessoais</h3>
<p><strong>Nome:</strong> {personalData.useSocialName && personalData.socialName
? personalData.socialName
: personalData.fullName}</p>
<p><strong>Email:</strong> {personalData.email}</p>
<p><strong>Telefone:</strong> {personalData.phone}</p>
<p><strong>LinkedIn:</strong> {personalData.linkedin}</p>
<p><strong>Resumo:</strong> {personalData.summary}</p>
</div>

{/* ----------------- HABILIDADES ----------------- */}
<div>
<h3 className="text-lg font-semibold">Habilidades</h3>
{habilidades.length === 0 ? (
<p className="text-gray-500">Nenhuma habilidade adicionada ainda.</p>
) : (
<ul className="list-disc pl-5 space-y-1">
{habilidades.map((item, index) => (
<li key={index}>
{item.nome} — <strong>{item.nivel}</strong>
</li>
))}
</ul>
)}
</div>
</div>
);
}
66 changes: 54 additions & 12 deletions src/context/CurriculoContext.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,85 @@
// Estado Global do curriculo
import React, { createContext, useState, useContext } from "react";

// 🔹 Tipo dos dados pessoais
type PersonalData = {
fullName: string;
socialName: string;
useSocialName: boolean; // ← novo campo
useSocialName: boolean;
email: string;
phone: string;
linkedin: string;
summary: string;
};

type ResumeContextType = {
// 🔹 Tipo das habilidades
type Habilidade = {
nome: string;
nivel: string;
};

// 🔹 O que o contexto vai disponibilizar
type CurriculoContextType = {
personalData: PersonalData;
setPersonalData: React.Dispatch<React.SetStateAction<PersonalData>>;
handleChange: (field: keyof PersonalData, value: string | boolean) => void;
habilidades: Habilidade[];
adicionarHabilidade: (h: Habilidade) => void;
removerHabilidade: (index: number) => void;
};

const ResumeContext = createContext<ResumeContextType | undefined>(undefined);
// Criação do contexto
export const CurriculoContext = createContext<CurriculoContextType | undefined>(undefined);

export const ResumeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
// Provider
export const CurriculoProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
// Estado para os dados pessoais
const [personalData, setPersonalData] = useState<PersonalData>({
fullName: "",
socialName: "",
useSocialName: false, // Caso de não usar
useSocialName: false,
email: "",
phone: "",
linkedin: "",
summary: "",
});

// Estado para as habilidades
const [habilidades, setHabilidades] = useState<Habilidade[]>([]);

// ✅ Função para atualizar os dados pessoais dinamicamente
const handleChange = (field: keyof PersonalData, value: string | boolean) => {
setPersonalData({
...personalData,
[field]: value,
});
};

// Funções para habilidades
const adicionarHabilidade = (h: Habilidade) => {
setHabilidades([...habilidades, h]);
};

const removerHabilidade = (index: number) => {
setHabilidades(habilidades.filter((_, i) => i !== index));
};

return (
<ResumeContext.Provider value={{ personalData, setPersonalData }}>
<CurriculoContext.Provider
value={{
personalData,
handleChange,
habilidades,
adicionarHabilidade,
removerHabilidade,
}}
>
{children}
</ResumeContext.Provider>
</CurriculoContext.Provider>
);
};

export const useResume = () => {
const context = useContext(ResumeContext);
if (!context) throw new Error("useResume deve ser usado dentro de ResumeProvider");
// Hook para usar o contexto
export const useCurriculo = () => {
const context = useContext(CurriculoContext);
if (!context) throw new Error("useCurriculo deve ser usado dentro de CurriculoProvider");
return context;
};
24 changes: 15 additions & 9 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
)
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";

// importa o provider do contexto
import { CurriculoProvider } from "./context/CurriculoContext";

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<CurriculoProvider>
<App />
</CurriculoProvider>
</React.StrictMode>
);