protecting routes for auth

This commit is contained in:
2026-05-08 15:16:04 +02:00
parent 965aa80fe0
commit 7c9e9c5001
15 changed files with 273 additions and 58 deletions

View File

@@ -17,7 +17,7 @@ import { validateToken } from './middleware/auth.js'
import { register, login } from './routes/auth.js' import { register, login } from './routes/auth.js'
import { deleteUser } from './routes/user.js' import { deleteUser } from './routes/user.js'
import { createOffer, deleteOffer } from './routes/offer.js' import { createOffer, deleteOffer, getOffers } from './routes/offer.js'
/* Test route so nxckwc can test axios*/ /* Test route so nxckwc can test axios*/
app.get('/users', async (req, res) => { app.get('/users', async (req, res) => {
@@ -26,12 +26,14 @@ app.get('/users', async (req, res) => {
res.json(user) res.json(user)
}) })
app.post('/login', login) app.post('/login', login)
app.post('/register', register) app.post('/register', register)
app.delete('/user/:userId', deleteUser) app.delete('/user/:userId', deleteUser)
app.post('/crear-oferta', validateToken, createOffer) app.post('/crear-oferta', validateToken, createOffer)
app.get('/offers', getOffers)
app.delete('/oferta/:projectId', validateToken, deleteOffer) app.delete('/oferta/:projectId', validateToken, deleteOffer)

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Oferta" ALTER COLUMN "url" SET DEFAULT '';

View File

@@ -45,7 +45,7 @@ model Oferta {
salario String @default("0") salario String @default("0")
contrato String contrato String
descripcion String descripcion String
url String url String @default("")
authorId Int authorId Int
author User @relation(fields: [authorId], references: [id]) author User @relation(fields: [authorId], references: [id])

View File

@@ -16,7 +16,7 @@ export const login = async (req, res) => {
const user = await prisma.user.findFirst({ where: { email } }); const user = await prisma.user.findFirst({ where: { email } });
if (!user || !await bcrypt.compare(password, user.password)) { if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(400).json({ error: 'Invalid credentials' }) return res.status(400).json({ error: 'Credenciales erroneas' })
} }
// Generate JWT // Generate JWT
@@ -35,31 +35,32 @@ export const login = async (req, res) => {
export const register = async (req, res) => { export const register = async (req, res) => {
// Get data // Get data
const {username, email, password } = req.body const {username, email, password, type } = req.body
// Check is not empty // Check is not empty
if (!username || !email || !password) { if (!username || !email || !password) {
return res.status(400).json({ error: 'All fields are required' }) return res.status(400).json({ error: 'Todos los campos son obligatorios' })
} }
// Validate data // Validate data
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (username.length < 3 ||username.length > 14) { return res.status(400).json({ error: 'Username must be between 3 and 14 characters' })} if (username.length < 3 ||username.length > 14) { return res.status(400).json({ error: 'El nombre de usuario debe tener entre 3 y 14 caracteres' })}
if (email.length > 30 || !emailRegex.test(email)) { return res.status(400).json({ error: 'Email is not valid' })} if (email.length > 30 || !emailRegex.test(email)) { return res.status(400).json({ error: 'Correo electronico no valido' })}
if (password.length < 6 || password.length > 32) { return res.status(400).json({ error: 'Password must be between 6 and 32 characters' })} if (password.length < 6 || password.length > 32) { return res.status(400).json({ error: 'La contraseña debe tener entre 6 y 32 caracteres' })}
if (type !== "candidato" && type !== "reclutador") { return res.status(400).json({ error: 'El tipo de cuenta no es valido' })}
// Check email and username doesnt exists // Check email and username doesnt exists
const userExists = await prisma.user.findFirst({ const userExists = await prisma.user.findFirst({
where: { where: {
OR: [ { email }, { username } ] OR: [ { email } ]
} }
}); });
// If username or email exists, send error // If username or email exists, send error
if (userExists) { if (userExists) {
return res.status(409).json({ return res.status(409).json({
error: 'User already exists' error: 'Este usuario ya existe'
}) })
} }
@@ -68,7 +69,7 @@ export const register = async (req, res) => {
// Create user // Create user
const user = await prisma.user.create({ const user = await prisma.user.create({
data: { username, email, password: hashedPassword, type: "candidato" } data: { username, email, password: hashedPassword, type }
}) })
// Generates token // Generates token

View File

@@ -1,15 +1,30 @@
import { prisma } from '../prisma.js' import { prisma } from '../prisma.js'
export const getOffers = async(req, res) => {
const offers = await prisma.oferta.findMany()
res.json(offers)
}
export const createOffer = async (req, res) => { export const createOffer = async (req, res) => {
const { titulo, empresa, ubicacion, salario, contrato, descripcion } = req.body; const { titulo, empresa, ubicacion, salario, contrato, descripcion } = req.body;
const authorId = req.user.id const authorId = req.user.id
if (!titulo || !empresa || !ubicacion || !salario || !contrato || !descripcion) return res.status(400).json({ error: ""}) if (!titulo || !empresa || !ubicacion || !salario || !contrato || !descripcion) return res.status(400).json({ error: ""})
if (titulo.length < 3 || titulo.length > 64) { return res.status(400).json({ error: 'El titulo debe tener entre 3 y 64 caracteres' })}
if (empresa.length < 2 || empresa.length > 32) { return res.status(400).json({ error: 'El nombre de la empresa debe tener entre 2 y 32 caracteres' })}
if (ubicacion.length < 3 || ubicacion.length > 16) { return res.status(400).json({ error: 'La ubicacion debe tener entre 3 y 16 caracteres' })}
if (salario.length < 3 || salario.length > 10) { return res.status(400).json({ error: 'El salario debe seguir este formato: 500€ o 20€/hora' })}
if (descripcion.length < 10 || descripcion.length > 256) { return res.status(400).json({ error: 'La descripcion debe tener entre 10 y 256 caracteres' })}
const contratosValidos = ["freelance", "completo", "medio"]
if (!contratosValidos.includes(contrato)) return res.status(400).json({ error: "contrato no valido" })
try { try {
const offer = await prisma.oferta.create({ const offer = await prisma.oferta.create({
data: { data: {
titulo, empresa, ubicacion, salario, descripcion titulo, empresa, ubicacion, salario, descripcion, contrato, authorId
} }
}) })
res.status(201).json(offer) res.status(201).json(offer)

View File

@@ -3,9 +3,12 @@ import { BrowserRouter, Routes, Route} from 'react-router-dom'
import Navbar from './components/Navbar' import Navbar from './components/Navbar'
import Home from './pages/Home' import Home from './pages/Home'
import NuevaVacante from './pages/NuevaVacante' import NuevaOferta from './pages/NuevaOferta'
import './index.css' import './index.css'
import Login from './pages/Login' import Login from './pages/Login'
import PublicRoute from './components/PublicRoute'
import PrivateRoute from './components/PrivateRoute'
import Register from './pages/Register'
function App() { function App() {
@@ -15,8 +18,9 @@ function App() {
<Navbar /> <Navbar />
<Routes> <Routes>
<Route path="/" element={<Home />} /> <Route path="/" element={<Home />} />
<Route path="/login" element={<Login />} /> <Route path="/login" element={<PublicRoute><Login /></PublicRoute>} />
<Route path="/nueva-oferta" element={<NuevaVacante />} /> <Route path="/register" element={<PublicRoute><Register /></PublicRoute>} />
<Route path="/nueva-oferta" element={<PrivateRoute><NuevaOferta /></PrivateRoute>} />
</Routes> </Routes>
</div> </div>
</BrowserRouter> </BrowserRouter>

View File

@@ -3,7 +3,7 @@ import { AuthContext } from "../context/AuthContext"
import { Link } from "react-router-dom" import { Link } from "react-router-dom"
function Navbar() { function Navbar() {
const { user } = useContext(AuthContext) const { user, logout } = useContext(AuthContext)
return ( return (
<nav className="py-10 flex justify-between"> <nav className="py-10 flex justify-between">
@@ -20,7 +20,7 @@ function Navbar() {
) : ( ) : (
<div className="flex gap-4 items-center"> <div className="flex gap-4 items-center">
<span className="text-white">Hola, {user.username}</span> <span className="text-white">Hola, {user.username}</span>
<button className="text-red-400 font-bold uppercase">Cerrar sesión</button> <button onClick={logout} className="text-red-400 font-bold uppercase">Cerrar sesión</button>
</div> </div>
)} )}
</div> </div>

View File

@@ -0,0 +1,14 @@
import { useContext } from "react"
import { AuthContext } from "../context/AuthContext"
import { Navigate, useLocation } from "react-router-dom"
const PrivateRoute = ({children}) => {
const { user, isLoading } = useContext(AuthContext)
const location = useLocation()
if (isLoading) return (null)
return user ? children : <Navigate to="/login" state={{ from: location.pathname }} />
}
export default PrivateRoute

View File

@@ -0,0 +1,11 @@
import { useContext } from "react"
import { AuthContext } from "../context/AuthContext"
import { Navigate } from "react-router-dom"
const PublicRoute = ({children}) => {
const { user } = useContext(AuthContext)
return user ? <Navigate to="/" /> : children
}
export default PublicRoute

1
frontend/src/config.js Normal file
View File

@@ -0,0 +1 @@
export const API_URL = "http://localhost:5000"

View File

@@ -4,10 +4,12 @@ export const AuthContext = createContext()
export function AuthProvider({ children }) { export function AuthProvider({ children }) {
const [user, setUser] = useState(null) const [user, setUser] = useState(null)
const [isLoading, setIsLoading] = useState(true)
useEffect(() => { useEffect(() => {
const user = localStorage.getItem('user') const user = localStorage.getItem('user')
if (user) setUser(JSON.parse(user)) if (user) setUser(JSON.parse(user))
setIsLoading(false)
}, []) }, [])
const login = (data) => { const login = (data) => {
@@ -23,7 +25,7 @@ export function AuthProvider({ children }) {
} }
return ( return (
<AuthContext.Provider value={{ user, login, logout }}> <AuthContext.Provider value={{ user, login, logout, isLoading }}>
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>
) )

View File

@@ -1,9 +1,26 @@
import { useEffect } from "react"
import { API_URL } from "../config"
import { useState } from "react"
function Home() { function Home() {
const [offers, setOffers] = useState([])
useEffect(() => {
fetch(`${API_URL}/offers`)
.then(res => res.json())
.then(data => setOffers(data))
}, [])
const contratos = {
freelance: "Freelance",
completo: "Jornada completa",
medio: "Media jornada"
}
return ( return (
<> <>
<div class="py-5 md:py-20 flex flex-col gap-4 border-b border-b-gray-700"> <div className="py-5 md:py-20 flex flex-col gap-4 border-b border-b-gray-700">
<h2 className="text-4xl font-semibold">HadiJobs</h2> <h2 className="text-4xl font-semibold">HadiJobs</h2>
<p>Encuentra y publica trabajos para desarrolladores</p> <p>Encuentra y publica trabajos para desarrolladores</p>
<a className="bg-[#00A4B6] py-3 px-10 rounded uppercase font-extrabold tracking-wider w-fit" <a className="bg-[#00A4B6] py-3 px-10 rounded uppercase font-extrabold tracking-wider w-fit"
@@ -12,29 +29,31 @@ function Home() {
<div className="py-5 md:py-20"> <div className="py-5 md:py-20">
<h2 className="text-4xl font-semibold pb-5">Lista de ofertas</h2> <h2 className="text-4xl font-semibold pb-5">Lista de ofertas</h2>
<div className="flex flex-row justify-between"> {offers.length === 0 && <p className="text-gray-500">No hay ofertas disponibles</p>}
{offers.map(offer => (
<div key={offer.id} className="flex flex-row items-center justify-between border-b border-gray-700 py-6 hover:bg-gray-800 cursor-pointer rounded px-4 transition">
<div className="flex flex-col flex-1">
<h3 className="text-xl ">{offer.empresa}</h3>
<p className="text-gray-500 ">{offer.titulo}</p>
</div>
<div className="flex flex-col"> <div className="flex flex-col flex-1">
<h3 className="text-xl ">Facebook</h3> <h3 className="text-gray-500 ">Ubicacion</h3>
<p className="text-gray-500 ">React developer</p> <p className="">{offer.ubicacion}</p>
</div>
<div className="flex flex-col flex-1">
<h3 className="text-gray-500">Contrato</h3>
<p>{contratos[offer.contrato]}</p>
</div>
<div className="flex flex-col">
<a className="bg-[#00A4B6] py-3 px-10 rounded uppercase font-extrabold tracking-wider w-fit"
href="#">Info</a>
</div>
</div> </div>
))}
<div className="flex flex-col">
<h3 className="text-gray-500 ">Ubicacion</h3>
<p className="">Remoto</p>
</div>
<div className="flex flex-col">
<h3 className="text-gray-500">Contrato</h3>
<p>Tiempo completo</p>
</div>
<div className="flex flex-col">
<a className="bg-[#00C897] py-3 px-10 rounded uppercase font-extrabold tracking-wider w-fit"
href="#">Info</a>
</div>
</div>
</div> </div>

View File

@@ -2,11 +2,13 @@ import { useContext } from "react"
import { useState } from "react" import { useState } from "react"
import { AuthContext } from "../context/AuthContext" import { AuthContext } from "../context/AuthContext"
import { useNavigate } from "react-router-dom" import { useNavigate } from "react-router-dom"
import { API_URL } from "../config"
function Login() { function Login() {
const [email, setEmail] = useState("") const [email, setEmail] = useState("")
const [password, setPassword] = useState("") const [password, setPassword] = useState("")
const [error, setError] = useState("")
const { login } = useContext(AuthContext) const { login } = useContext(AuthContext)
const navigate = useNavigate() const navigate = useNavigate()
@@ -14,7 +16,7 @@ function Login() {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault() e.preventDefault()
const res = await fetch('http://localhost:5000/login', { const res = await fetch(`${API_URL}/login`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }) body: JSON.stringify({ email, password })
@@ -26,6 +28,7 @@ function Login() {
login(data) login(data)
navigate('/') navigate('/')
} else { } else {
setError(data.error)
console.error(data.error) console.error(data.error)
} }
} }
@@ -38,6 +41,8 @@ function Login() {
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<h3 className="text-center text-5xl mb-16">Inicia sesion</h3> <h3 className="text-center text-5xl mb-16">Inicia sesion</h3>
{ error && <p className="text-red-400">{error}</p> }
<div className="flex flex-col mb-8"> <div className="flex flex-col mb-8">
<label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase"> <label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase">
Correo Correo
@@ -53,13 +58,13 @@ function Login() {
Contraseña Contraseña
</label> </label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} <input type="password" value={password} onChange={(e) => setPassword(e.target.value)}
placeholder="Escribe su contraseña..." required placeholder="Escriba su contraseña..." required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal" className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/> />
</div> </div>
<div> <div>
<input type="submit" value="Acceder" class="bg-[#00A4B6] hover:bg-[#027481] transition cursor-pointer py-3 px-10 rounded uppercase font-extrabold tracking-wider w-full" /> <input type="submit" value="Acceder" className="bg-[#00A4B6] hover:bg-[#027481] transition cursor-pointer py-3 px-10 rounded uppercase font-extrabold tracking-wider w-full" />
</div> </div>
</form> </form>

View File

@@ -1,23 +1,59 @@
import { useState } from "react"
import { useNavigate } from "react-router-dom"
import { API_URL } from "../config"
function NuevaVacante() { function NuevaOferta() {
const navigate = useNavigate()
const [titulo, setTitulo] = useState("")
const [empresa, setEmpresa] = useState("")
const [salario, setSalario] = useState("")
const [contrato, setContrato] = useState("")
const [ubicacion, setUbicacion] = useState("")
const [descripcion, setDescripcion] = useState("")
const [error, setError] = useState("")
const handleSubmit = async (e) => {
e.preventDefault()
setError("")
const res = await fetch(`${API_URL}/crear-oferta`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${localStorage.getItem("token")}`
},
body: JSON.stringify({titulo, empresa, ubicacion, salario, contrato, descripcion})
})
const data = await res.json()
if (res.ok) {
navigate('/')
} else {
setError(data.error)
}
}
return ( return (
<> <>
<div className="py-5 md:py-20 flex flex-col gap-4 border-b border-b-gray-700"> <div className="py-5 md:py-20 flex flex-col gap-4 border-b border-b-gray-700">
<h2 className="text-4xl font-semibold">Nueva vacante</h2> <h2 className="text-4xl font-semibold">Nueva oferta</h2>
<p>Rellena el formulario para publicar tu oferta de trabajo</p> <p>Rellena el formulario para publicar tu oferta de trabajo</p>
</div> </div>
<div className="max-w-[800px] mx-auto p-10"> <div className="max-w-[800px] mx-auto p-10">
<form> <form onSubmit={handleSubmit} >
<h3 className="text-gray-500 mb-8">Informacion general</h3> <h3 className="text-gray-500 mb-8">Informacion general</h3>
{ error && <p className="text-red-500">{error}</p>}
<div className="flex mb-8"> <div className="flex mb-8">
<label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase"> <label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase">
Titulo Titulo
</label> </label>
<input type="text" name="titulo" <input type="text" name="titulo" value={titulo} onChange={(e) => setTitulo(e.target.value)}
placeholder="Ej: React Developer" required placeholder="Ej: React Developer" required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal" className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/> />
@@ -27,7 +63,7 @@ function NuevaVacante() {
<label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase"> <label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase">
Empresa Empresa
</label> </label>
<input type="text" name="empresa" <input type="text" name="empresa" value={empresa} onChange={(e) => setEmpresa(e.target.value)}
placeholder="Ej: HadiES" required placeholder="Ej: HadiES" required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal" className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/> />
@@ -37,7 +73,7 @@ function NuevaVacante() {
<label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase"> <label className="w-[9rem] pt-2 text-white font-bold shrink-0 uppercase">
Ubicacion Ubicacion
</label> </label>
<input type="text" name="ubicacion" <input type="text" name="ubicacion" value={ubicacion} onChange={(e) => setUbicacion(e.target.value)}
placeholder="Ej: España... Global..." required placeholder="Ej: España... Global..." required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal" className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/> />
@@ -48,7 +84,7 @@ function NuevaVacante() {
Salario (EUR) Salario (EUR)
</label> </label>
<input type="text" name="salario" <input type="text" name="salario"
placeholder="Ej: 50€/hora o 200€" required placeholder="Ej: 50€/hora o 200€" required value={salario} onChange={(e) => setSalario(e.target.value)}
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal" className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/> />
</div> </div>
@@ -58,8 +94,9 @@ function NuevaVacante() {
Contrato Contrato
</label> </label>
<select name="contrato" className="bg-gray-200 text-black w-full text-center font-normal"> <select name="contrato" defaultValue={0} value={contrato} onChange={(e) => setContrato(e.target.value)}
<option value="" selected disabled>-- Selecciona una opcion --</option> className="bg-gray-200 text-black w-full text-center font-normal">
<option value="" disabled>-- Selecciona una opcion --</option>
<option value="freelance">Freelance</option> <option value="freelance">Freelance</option>
<option value="completo">Jornada completa</option> <option value="completo">Jornada completa</option>
<option value="medio">Media jornada</option> <option value="medio">Media jornada</option>
@@ -69,14 +106,14 @@ function NuevaVacante() {
<div className="flex flex-col gap-4 mb-8"> <div className="flex flex-col gap-4 mb-8">
<h3 className="font-bold uppercase">Descripcion del puesto</h3> <h3 className="font-bold uppercase">Descripcion del puesto</h3>
<textarea name="descripcion" <textarea name="descripcion" value={descripcion} onChange={(e) => setDescripcion(e.target.value)}
placeholder="Describe el puesto..." placeholder="Describe el puesto..."
className="bg-gray-200 flex-1 px-5 py-3 rounded text-black border-none font-normal min-h-[200px]" className="bg-gray-200 flex-1 px-5 py-3 rounded text-black border-none font-normal min-h-[200px]"
/> />
</div> </div>
<div class=""> <div className="">
<input type="submit" value="Publicar" class="bg-[#00A4B6] py-3 px-10 rounded uppercase font-extrabold tracking-wider w-full" /> <input type="submit" value="Publicar" className="bg-[#00A4B6] hover:bg-[#027481] transition py-3 px-10 rounded uppercase font-extrabold tracking-wider w-full cursor-pointer" />
</div> </div>
</form> </form>
@@ -85,4 +122,4 @@ function NuevaVacante() {
) )
} }
export default NuevaVacante export default NuevaOferta

View File

@@ -0,0 +1,102 @@
import { useContext } from "react"
import { useState } from "react"
import { AuthContext } from "../context/AuthContext"
import { useNavigate } from "react-router-dom"
import { API_URL } from "../config"
function Register() {
const [username, setUsername] = useState("")
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [type, setType] = useState("")
const [error, setError] = useState("")
const { login } = useContext(AuthContext)
const navigate = useNavigate()
const handleSubmit = async (e) => {
e.preventDefault()
if (!type) return setError("Debes seleccionar un tipo de cuenta ('Busco trabajo / Ofrezco trabajo')")
const res = await fetch(`${API_URL}/register`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, email, password })
})
const data = await res.json()
if (res.ok) {
login(data)
navigate('/')
} else {
setError(data.error)
console.error(data.error)
}
}
return (
<>
<div className="max-w-[800px] mx-auto p-20">
<form onSubmit={handleSubmit}>
<h3 className="text-center text-5xl mb-16">Registrate</h3>
{ error && <p className="text-red-400">{error}</p> }
<div className="flex flex-col mb-8">
<label className="pt-2 text-white font-bold shrink-0 uppercase">
Nombre de usuario
</label>
<input type="username" value={username} onChange={(e) => setUsername(e.target.value)}
placeholder="Escriba su nombre de usuario..." required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/>
</div>
<div className="flex flex-col mb-8">
<label className="pt-2 text-white font-bold shrink-0 uppercase">
Correo
</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)}
placeholder="Escriba su email..." required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/>
</div>
<div className="flex flex-col mb-8">
<label className="pt-2 text-white font-bold shrink-0 uppercase">
Contraseña
</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)}
placeholder="Escriba su contraseña..." required
className="bg-gray-200 flex-1 px-5 py-1 rounded text-black w-full border-none font-normal"
/>
</div>
<div className="flex flex-col mb-8">
<label className="pt-2 text-white font-bold shrink-0 uppercase">
¿Buscas trabajo o quieres publicar ofertas?
</label>
<div className="flex gap-5 justify-between my-5">
<button className={`w-1/2 py-3 transition font-bold uppercase hover:bg-[#00A4B6] active:brightness-125 ${type === "candidato" ? "bg-[#00A4B6]" : "bg-gray-600"}`}
type="button" onClick={() => setType("candidato")}>Busco</button>
<button className={`w-1/2 py-3 transition font-bold uppercase hover:bg-[#00A4B6] active:brightness-125 ${type === "reclutador" ? "bg-[#00A4B6]" : "bg-gray-600"}`}
type="button" onClick={() => setType("reclutador")}>Publico</button>
</div>
</div>
<div>
<input type="submit" value="Crear cuenta" className="bg-[#00A4B6] hover:bg-[#027481] transition cursor-pointer py-3 px-10 rounded uppercase font-extrabold tracking-wider w-full" />
</div>
</form>
</div>
</>
)
}
export default Register