Skip to content

Commit

Permalink
Merge pull request #54 from WildCodeSchool-2023-09/feat_validate_job
Browse files Browse the repository at this point in the history
Feat validate job
  • Loading branch information
jujuck authored Feb 8, 2024
2 parents ae53303 + e2a96da commit 19d526f
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 69 deletions.
20 changes: 18 additions & 2 deletions backend/src/controllers/jobControllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,25 @@ const browseLatest = async (req, res, next) => {
next(err);
}
};

// The E of BREAD - Edit (Update) operation
// This operation is not yet implemented
const edit = async (req, res, next) => {
// Extract the job data from the request body
const job = req.body;
try {
const result = await tables.job.update(req.params.id, job);

// If the job is not found, respond with HTTP 404 (Not Found)
if (result.affectedRows === 1) {
res.sendStatus(204);
} else {
res.sendStatus(404);
}
} catch (err) {
// Pass any errors to the error-handling middleware
next(err);
}
};
// The A of BREAD - Add (Create) operation
const add = async (req, res, next) => {
// Extract the job data from the request body
Expand Down Expand Up @@ -157,7 +173,7 @@ module.exports = {
readByCompany,
readByCompanyJob,
browseLatest,
// edit,
edit,
add,
destroy,
};
15 changes: 12 additions & 3 deletions backend/src/models/JobManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,18 @@ class JobManager extends AbstractManager {
// The U of CRUD - Update operation
// TODO: Implement the update operation to modify an existing job

// async update(job) {
// ...
// }
async update(id, job) {
// Execute the SQL SELECT query to retrieve a specific job by its ID
// eslint-disable-next-line no-param-reassign
delete job.created_at;
const [result] = await this.database.query(
`UPDATE ${this.table} set ? WHERE id = ?`,
[job, id]
);

// Return the first row of the result, which represents the item
return result;
}

// The D of CRUD - Delete operation
// TODO: Implement the delete operation to remove an job by its ID
Expand Down
22 changes: 10 additions & 12 deletions backend/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const validateUser = require("./validators/validateUser");
const validateAccount = require("./validators/validateAccount");
const validateCompany = require("./validators/validateCompany");
const validateCV = require("./validators/validateCV");
const validateJob = require("./validators/validateJob");

// ROUTES GET
router.get("/jobs", jobControllers.browse);
Expand All @@ -32,7 +33,7 @@ router.get("/companies", companyControllers.browse);
router.get(
"/consultants",
checkCredentials,
checkAdmin,
checkConsultant,
userControllers.getConsultant
);
router.get("/roles", checkCredentials, checkAdmin, roleControllers.browse);
Expand Down Expand Up @@ -85,8 +86,13 @@ router.get(
router.get("/roles/:id", checkCredentials, checkAdmin, roleControllers.read);
router.get("/users/:id", checkCredentials, userControllers.read);

// ROUTES POST
router.post("/jobs", checkCredentials, checkConsultant, jobControllers.add);
router.post(
"/jobs",
validateJob,
checkCredentials,
checkConsultant,
jobControllers.add
);
router.post("/login", validateUser, userControllers.login);
router.post("/register", validateUser, userControllers.add);
router.post(
Expand Down Expand Up @@ -145,15 +151,7 @@ router.put(
validateCV,
userControllers.updateProfileCV
);
router.put(
"/application/:id",
checkCredentials,
checkConsultant,
applicationControllers.edit
);

router.post("/logout", userControllers.logout);

router.put("/jobs/:id", checkCredentials, checkConsultant, jobControllers.edit);
/* ************************************************************************* */

module.exports = router;
35 changes: 35 additions & 0 deletions backend/src/validators/validateJob.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const Joi = require("joi");

const schema = Joi.object({
id: Joi.number(),
company_id: Joi.number(),
consultant_id: Joi.number(),
title: Joi.string().required(),
description_mission: Joi.string().required(),
description_about_candidate: Joi.string().required(),
description_position: Joi.string().required(),
description_advantages: Joi.string().required(),
description_process: Joi.string().required(),
language: Joi.string().required(),
salary: Joi.string().required(),
location: Joi.string().required(),
working_type: Joi.string().required(),
starting_date: Joi.date().required(),
position_category: Joi.string().required(),
contract_type: Joi.string().required(),
position_requirements: Joi.string().required(),
created_at: Joi.string(),
});

const validateJob = (req, res, next) => {
delete req.body.id;
const { error } = schema.validate(req.body);

if (error) {
res.status(422).json(error);
} else {
next();
}
};

module.exports = validateJob;
10 changes: 10 additions & 0 deletions frontend/src/components/JobCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ function JobCard({ job, cardStyle, refresh, isUserPage }) {
>
<h3 className={`${cardStyle}-title`}>{job.title}</h3>
</Link>
{!isUserPage ? (
<Link to={`/consultants/administration/job/${job.job_id}`}>
<button className="connection-button delete-card" type="button">
Editer
</button>
</Link>
) : (
""
)}
{!isUserPage ? (
<button
className="connection-button delete-card"
Expand All @@ -62,6 +71,7 @@ function JobCard({ job, cardStyle, refresh, isUserPage }) {
</button>
)}
</div>

<div className={`${cardStyle}-body`}>
<div className={`${cardStyle}-requirement`}>
<p className="job-card-language">{job.language}</p>
Expand Down
54 changes: 35 additions & 19 deletions frontend/src/components/SelectConsultant.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,52 @@ import connexion from "../services/connexion";

import colorStyles from "../assets/selectStyle";

function SelectConsultant({ label, url, criteria, handleSelect, name }) {
const [list, setList] = useState([]);

const getList = async () => {
try {
const result = await connexion.get(`/${url}`).then((res) => res.data);
setList(result);
} catch (error) {
console.error(error);
}
};
function SelectConsultant({
label,
url,
criteria,
handleSelect,
name,
company,
}) {
const [options, setOptions] = useState([]);

useEffect(() => {
const getList = async () => {
try {
const result = await connexion.get(`/${url}`);
const formattedOptions = result.data.map((item) => ({
value: item.id,
label: item[criteria],
}));
setOptions(formattedOptions);
} catch (error) {
console.error(error);
}
};

getList();
}, []);
}, [url, criteria]);

const standardizeEvent = (option) => {
handleSelect({ target: { name, value: option.value } });
const standardizeEvent = (selectedOption) => {
handleSelect({
target: { name, value: selectedOption ? selectedOption.value : "" },
});
};

const value = company
? options.find((option) => String(option.value) === String(company))
: null;

return (
<label className="label">
{label}
<Select
onChange={standardizeEvent}
required
options={list.map((el) => ({
value: el.id,
label: el[criteria],
}))}
value={value}
options={options}
styles={colorStyles}
isClearable
/>
</label>
);
Expand All @@ -47,6 +62,7 @@ SelectConsultant.propTypes = {
criteria: PropTypes.string.isRequired,
handleSelect: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
company: PropTypes.string.isRequired,
};

export default SelectConsultant;
10 changes: 8 additions & 2 deletions frontend/src/components/SelectFromList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ import PropTypes from "prop-types";
import Select from "react-select";
import colorStyles from "../assets/selectStyle";

function SelectFromList({ label, dataSet, handleSelect, name }) {
function SelectFromList({ label, dataSet, handleSelect, name, find }) {
const standardizeEvent = (option) => {
handleSelect({ target: { name, value: option.value } });
};

const currentValue =
find && dataSet.includes(find) ? { value: find, label: find } : null;

return (
<label className="label">
{label}
Expand All @@ -14,9 +18,10 @@ function SelectFromList({ label, dataSet, handleSelect, name }) {
onChange={standardizeEvent}
required
options={dataSet.map((el) => ({
value: el.id,
value: el,
label: el,
}))}
value={currentValue}
styles={colorStyles}
/>
</label>
Expand All @@ -28,6 +33,7 @@ SelectFromList.propTypes = {
dataSet: PropTypes.arrayOf(PropTypes.string).isRequired,
handleSelect: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
find: PropTypes.string.isRequired,
};

export default SelectFromList;
4 changes: 2 additions & 2 deletions frontend/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import App from "./App";
import FormLogin from "./pages/FormLogin";
import FormRegister from "./pages/FormRegister";
import HomePage from "./pages/HomePage";
import AdminJob from "./pages/AdminJobs";
import AdminJobs from "./pages/AdminJobs";

import AllJobsPage from "./pages/AllJobsPage";
import ConsultantPage from "./pages/layout/ConsultantPage";
Expand Down Expand Up @@ -92,7 +92,7 @@ const router = createBrowserRouter([
children: [
{
path: "job/:id",
element: <AdminJob />,
element: <AdminJobs />,
},
{
path: "job",
Expand Down
Loading

0 comments on commit 19d526f

Please sign in to comment.