Commit f52685a2 authored by Rishad M.H.M's avatar Rishad M.H.M

Merge branch 'IT20613686/Job-posting' into 'master'

Job posting endpoint and integration

See merge request !5
parents 9a21a2a1 f4129b48
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const jobSchema = new Schema({
jobID: {
type: String,
},
title: {
type: String,
required: true,
},
company: {
type: String,
required: true,
},
location: {
type: String,
required: true,
},
workType: {
type: String,
required: true,
},
term: {
type: String,
required: true,
},
salary: {
type: Number,
required: true,
},
duration: {
type: String,
required: true,
},
deadline: {
type: Date,
required: true,
},
logo: {
type: String,
required: true,
},
responsibilities: {
type: String,
required: true,
},
qualifications: {
type: String,
required: true,
},
aboutJob: {
type: String,
required: true,
},
});
const Job = mongoose.model("Job", jobSchema);
module.exports = Job;
const router = require("express").Router();
const multer = require("multer");
let Job = require("../models/job");
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "uploads");
},
filename: (req, file, cb) => {
cb(null, Date.now() + file.originalname);
},
});
const upload = multer({
storage: storage,
}).single("logo");
router.post("/add", upload, async (req, res) => {
const newJob = new Job({
jobID: req.body,
title: req.body.title,
company: req.body.company,
location: req.body.location,
workType: req.body.workType,
term: req.body.term,
salary: req.body.salary,
duration: req.body.duration,
deadline: req.body.deadline,
logo: req.file.filename,
responsibilities: req.body.responsibilities,
qualifications: req.body.qualifications,
aboutJob: req.body.aboutJob,
});
const totalNumberOfJobInDb = await Job.countDocuments();
// convert number to string, so we can concatenate 0s easily...
let numberToString = totalNumberOfJobInDb.toString();
// If length of number string is less than 5 then add leading 0s in nuberToString
if (numberToString.length < 3) {
for (let i = numberToString.length; i < 3; i++) {
numberToString = "0" + numberToString;
}
}
newJob.jobID = `JID${numberToString}`;
newJob
.save()
.then(() => {
res.status(200).send({ status: "Job added" });
})
.catch((err) => {
console.log(err);
res
.status(500)
.send({ status: "Error with adding job", error: err.message });
});
});
module.exports = router;
\ No newline at end of file
......@@ -5,8 +5,7 @@ const cors = require("cors");
const connectDB = require("./config/db");
const dotenv = require("dotenv");
const userRoutes = require("./routes/userRoutes");
// const hardwareRouter = require("./routes/hardwares.js");
// const hardwareItemRouter = require("./routes/hardwareItems.js");
const jobRoutes = require("./routes/jobs")
const { notFound, errorHandler } = require("./middlewares/errorMiddleware");
const app = express();
......@@ -21,8 +20,7 @@ app.use(bodyparser.urlencoded({ extended: true }));
app.use('/uploads', express.static('./uploads'));
app.use("/api/users", userRoutes);
// app.use("/hardware", hardwareRouter);
// app.use("/hardwareItem", hardwareItemRouter);
app.use("/job", jobRoutes)
app.use(notFound);
app.use(errorHandler);
......
This diff is collapsed.
......@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@redux-devtools/extension": "^3.2.6",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
......@@ -13,8 +14,11 @@
"react-datepicker": "^4.21.0",
"react-dom": "^18.2.0",
"react-quill": "^2.0.0",
"react-redux": "^8.1.3",
"react-router-dom": "^6.17.0",
"react-scripts": "5.0.1",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"web-vitals": "^2.1.4"
},
"scripts": {
......
import React, {useState} from 'react'
import React, {useState, useCallback} from 'react'
import DatePicker from "react-datepicker";
import ReactQuill from "react-quill";
import axios from 'axios';
import "react-datepicker/dist/react-datepicker.css";
import "react-quill/dist/quill.snow.css";
import './JobPosting.css'
function JobPosting() {
const [selectedDate, setSelectedDate] = useState(new Date());
const [title, setTitle] = useState("");
const [company, setCompany] = useState("");
const [location, setLocation] = useState("");
const [workType, setWorkType] = useState("");
const [term, setTerm] = useState("");
const [salary, setSalary] = useState("");
const [duration, setDuration] = useState("");
const [deadline, setDeadline] = useState(new Date());
const [logo, setLogo] = useState("");
const [responsibilities, setResponsibilities] = useState("");
const [qualifications, setQualifications] = useState("");
const [aboutJob, setAboutJob] = useState("");
const handleQuillChange = (content, setContent) => {
// Remove <p> tags from the content
const strippedContent = content.replace(/<p><\/p>/g, ""); // Remove empty <p> tags
const finalContent = strippedContent.replace(/<p>(.*?)<\/p>/g, "$1\n"); // Preserve content structure
setContent(finalContent.trim());
};
function sendData(e) {
e.preventDefault();
const formData = new FormData();
formData.append("title", title);
formData.append("company", company);
formData.append("location", location);
formData.append("workType", workType);
formData.append("term", term);
formData.append("salary", salary);
formData.append("duration", duration);
formData.append("deadline", deadline);
formData.append("logo", logo);
formData.append("responsibilities", responsibilities);
formData.append("qualifications", qualifications);
formData.append("aboutJob", aboutJob);
axios
.post("http://localhost:8070/job/add", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
.then(() => {
alert("Job posted successfully");
window.location.reload();
})
.catch((err) => {
alert(err);
});
}
return (
<div className="jobPosting">
<form className="form-jobPosting">
<form className="form-jobPosting" onSubmit={sendData}>
<div className="form-group">
<label htmlFor="jobTitle">Job Title</label>
<input type="text" className="form-control" id="jobTitle" />
<input
type="text"
className="form-control"
id="jobTitle"
required
onChange={(e) => {
setTitle(e.target.value);
}}
/>
</div>
<div className="form-group">
<label htmlFor="companyName">Company Name</label>
<select className="form-control" id="companyName">
<select
className="form-control"
id="companyName"
required
onChange={(e) => {
setCompany(e.target.value);
}}
>
<option value="" disabled selected>
Select Company
</option>
<option value="company1">Company 1</option>
<option value="company2">Company 2</option>
<option>InstaHire</option>
</select>
</div>
<div className="form-group">
<label htmlFor="location">Location</label>
<select className="form-control" id="location">
<select
className="form-control"
id="location"
required
onChange={(e) => {
setLocation(e.target.value);
}}
>
<option value="" disabled selected>
Select location
</option>
<option value="location1">Location 1</option>
<option value="location2">Location 2</option>
<option>No:365, Galle Road, Colombo 03</option>
</select>
</div>
<div className="d-flex">
<div className="form-group col-md-6">
<label htmlFor="workType">Work Type</label>
<select className="form-control" id="workType">
<select
className="form-control"
id="workType"
required
onChange={(e) => {
setWorkType(e.target.value);
}}
>
<option value="" disabled selected>
Select work type
</option>
<option value="workType1">Work Type 1</option>
<option value="workType2">Work Type 2</option>
<option>On-site</option>
<option>Hybrid</option>
<option>Online</option>
</select>
</div>
<div className="form-group col-md-6" style={{ paddingLeft: "5px" }}>
<label htmlFor="term">Term</label>
<select className="form-control" id="term">
<select
className="form-control"
id="term"
required
onChange={(e) => {
setTerm(e.target.value);
}}
>
<option value="" disabled selected>
Select term
</option>
<option value="term1">Term 1</option>
<option value="term2">Term 2</option>
<option>Full-time</option>
<option>Part-time</option>
</select>
</div>
</div>
<div className="d-flex">
<div className="form-group col-md-6">
<label htmlFor="salary">Salary</label>
<input type="number" className="form-control" id="salary" />
<input
type="number"
className="form-control"
id="salary"
required
onChange={(e) => {
setSalary(e.target.value);
}}
/>
</div>
<div className="form-group col-md-6" style={{ paddingLeft: "5px" }}>
<label htmlFor="duration">Duration</label>
<select className="form-control" id="duration">
<select
className="form-control"
id="duration"
required
onChange={(e) => {
setDuration(e.target.value);
}}
>
<option value="" disabled selected>
Select duration
</option>
<option value="duration1">Duration 1</option>
<option value="duration2">Duration 2</option>
<option>6 months</option>
<option>1 year</option>
<option>2 year</option>
</select>
</div>
</div>
......@@ -83,8 +183,8 @@ function JobPosting() {
<DatePicker
className="form-control"
id="deadline"
selected={selectedDate}
onChange={(date) => setSelectedDate(date)}
selected={deadline}
onChange={(date) => setDeadline(date)}
/>
</div>
</div>
......@@ -95,9 +195,13 @@ function JobPosting() {
<label htmlFor="logo">Logo</label>
<input
type="file"
name="logo"
className="form-control-file"
id="logo"
accept="image/*"
required
onChange={(e) => {
setLogo(e.target.files[0]);
}}
/>
</div>
</div>
......@@ -106,7 +210,9 @@ function JobPosting() {
<ReactQuill
theme="snow"
value={responsibilities}
onChange={setResponsibilities}
onChange={(content) =>
handleQuillChange(content, setResponsibilities)
}
/>
</div>
......@@ -115,16 +221,24 @@ function JobPosting() {
<ReactQuill
theme="snow"
value={qualifications}
onChange={setQualifications}
onChange={(content) =>
handleQuillChange(content, setQualifications)
}
/>
</div>
<div className="form-group">
<label htmlFor="aboutJob">About the Job</label>
<ReactQuill theme="snow" value={aboutJob} onChange={setAboutJob} />
<ReactQuill
theme="snow"
value={aboutJob}
onChange={(content) => handleQuillChange(content, setAboutJob)}
/>
</div>
<div style={{ paddingTop: "10px" }}>
<button className='btn btn-primary' type="submit">Post Job</button>
<button className="btn btn-primary" type="submit">
Post Job
</button>
</div>
</form>
</div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment