Oferta page
This commit is contained in:
@@ -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, getOffers } from './routes/offer.js'
|
import { createOffer, deleteOffer, getOffers, getOffer } 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) => {
|
||||||
@@ -33,8 +33,9 @@ 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.get('/ofertas', getOffers)
|
||||||
app.delete('/oferta/:projectId', validateToken, deleteOffer)
|
app.get('/oferta/:offerId', validateToken, getOffer)
|
||||||
|
app.delete('/oferta/:offerId', validateToken, deleteOffer)
|
||||||
|
|
||||||
|
|
||||||
app.listen(port, () => {
|
app.listen(port, () => {
|
||||||
|
|||||||
@@ -6,16 +6,27 @@ export const getOffers = async(req, res) => {
|
|||||||
res.json(offers)
|
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) => {
|
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
|
||||||
|
|
||||||
|
const salarioRegex = /\d/
|
||||||
|
|
||||||
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 (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 (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 (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 (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' })}
|
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"]
|
const contratosValidos = ["freelance", "completo", "medio"]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import Login from './pages/Login'
|
|||||||
import PublicRoute from './components/PublicRoute'
|
import PublicRoute from './components/PublicRoute'
|
||||||
import PrivateRoute from './components/PrivateRoute'
|
import PrivateRoute from './components/PrivateRoute'
|
||||||
import Register from './pages/Register'
|
import Register from './pages/Register'
|
||||||
|
import Oferta from './pages/Oferta'
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ function App() {
|
|||||||
<Route path="/login" element={<PublicRoute><Login /></PublicRoute>} />
|
<Route path="/login" element={<PublicRoute><Login /></PublicRoute>} />
|
||||||
<Route path="/register" element={<PublicRoute><Register /></PublicRoute>} />
|
<Route path="/register" element={<PublicRoute><Register /></PublicRoute>} />
|
||||||
<Route path="/nueva-oferta" element={<PrivateRoute><NuevaOferta /></PrivateRoute>} />
|
<Route path="/nueva-oferta" element={<PrivateRoute><NuevaOferta /></PrivateRoute>} />
|
||||||
|
<Route path="/oferta/:offerId" element={<PrivateRoute><Oferta /></PrivateRoute>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function Home() {
|
|||||||
const [offers, setOffers] = useState([])
|
const [offers, setOffers] = useState([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch(`${API_URL}/offers`)
|
fetch(`${API_URL}/ofertas`)
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(data => setOffers(data))
|
.then(data => setOffers(data))
|
||||||
}, [])
|
}, [])
|
||||||
@@ -50,7 +50,7 @@ function Home() {
|
|||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<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"
|
||||||
href="#">Info</a>
|
href={`/oferta/${offer.id}`}>Info</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
74
frontend/src/pages/Oferta.jsx
Normal file
74
frontend/src/pages/Oferta.jsx
Normal 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
|
||||||
Reference in New Issue
Block a user