Oferta page

This commit is contained in:
2026-05-08 17:24:36 +02:00
parent 7c9e9c5001
commit f5c9713d1d
5 changed files with 93 additions and 5 deletions

View File

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

View File

@@ -6,16 +6,27 @@ export const getOffers = async(req, res) => {
res.json(offers)
}
export const getOffer = async(req, res) => {
const { offerId } = req.params
const id = parseInt(offerId)
const offer = await prisma.oferta.findFirst({ where: { id } })
res.json(offer);
}
export const createOffer = async (req, res) => {
const { titulo, empresa, ubicacion, salario, contrato, descripcion } = req.body;
const authorId = req.user.id
const salarioRegex = /\d/
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 (!salarioRegex.test(salario)) return res.status(400).json({ error: "El salario debe contener un numero"})
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"]

View File

@@ -9,6 +9,7 @@ import Login from './pages/Login'
import PublicRoute from './components/PublicRoute'
import PrivateRoute from './components/PrivateRoute'
import Register from './pages/Register'
import Oferta from './pages/Oferta'
function App() {
@@ -21,6 +22,7 @@ function App() {
<Route path="/login" element={<PublicRoute><Login /></PublicRoute>} />
<Route path="/register" element={<PublicRoute><Register /></PublicRoute>} />
<Route path="/nueva-oferta" element={<PrivateRoute><NuevaOferta /></PrivateRoute>} />
<Route path="/oferta/:offerId" element={<PrivateRoute><Oferta /></PrivateRoute>} />
</Routes>
</div>
</BrowserRouter>

View File

@@ -7,7 +7,7 @@ function Home() {
const [offers, setOffers] = useState([])
useEffect(() => {
fetch(`${API_URL}/offers`)
fetch(`${API_URL}/ofertas`)
.then(res => res.json())
.then(data => setOffers(data))
}, [])
@@ -50,7 +50,7 @@ function Home() {
<div className="flex flex-col">
<a className="bg-[#00A4B6] py-3 px-10 rounded uppercase font-extrabold tracking-wider w-fit"
href="#">Info</a>
href={`/oferta/${offer.id}`}>Info</a>
</div>
</div>
))}

View File

@@ -0,0 +1,74 @@
import { useEffect, useState } from 'react'
import { useParams } from 'react-router-dom'
import { API_URL } from '../config'
const Oferta = () => {
const { offerId } = useParams()
const [offer, setOffer] = useState()
useEffect(() => {
fetch(`${API_URL}/oferta/${offerId}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(data => setOffer(data))
}, [])
const contratos = {
freelance: "Freelance",
completo: "Jornada completa",
medio: "Media jornada"
}
if (!offer) return <p>Cargando...</p>
return (
<>
<div className="flex justify-between py-10 md:py-20 items-center">
<h2 className="text-4xl font-semibold pb-5">Oferta {offer?.titulo}</h2>
<div>
<a className="bg-[#00A4B6] hover:bg-[#027481] transition cursor-pointer py-3 px-10 rounded uppercase font-extrabold tracking-wider w-full">Enviar CV</a>
</div>
</div>
<div className="py-5">
<div className="flex flex-row items-center justify-between border-b border-t border-gray-700 py-20 rounded px-4 transition">
<div className="flex flex-col flex-1">
<h3 className="text-xl text-gray-500">Empresa</h3>
<p className="text-2xl">{offer.empresa}</p>
</div>
<div className="flex flex-col flex-1">
<h3 className="text-gray-500 text-xl">Ubicacion</h3>
<p className="text-2xl">{offer.ubicacion}</p>
</div>
<div className="flex flex-col flex-1">
<h3 className="text-gray-500 text-xl">Contrato</h3>
<p className="text-2xl">{contratos[offer.contrato]}</p>
</div>
<div className="flex flex-col flex-1">
<h3 className="text-gray-500 text-xl">Salario</h3>
<p className="text-2xl">{offer.salario} </p>
</div>
</div>
</div>
<div className="py-5">
<div className="flex flex-row items-center justify-between border-b border-gray-700 py-20 rounded px-4 transition">
<div className="flex flex-col flex-1">
<h3 className="text-xl text-gray-500">Descripcion del puesto</h3>
<p className="text-2xl">{offer.descripcion}</p>
</div>
</div>
</div>
</>
)
}
export default Oferta