diff --git a/backend/database/data/applicationStatus.json b/backend/database/data/applicationStatus.json
index 39262dd..780e9eb 100644
--- a/backend/database/data/applicationStatus.json
+++ b/backend/database/data/applicationStatus.json
@@ -1 +1 @@
-[{ "label": "Accepté" }, { "label": "Refusé" }, { "label": "En cours" }]
+[{ "label": "Accepté" }, { "label": "En cours" }, { "label": "Refusé" }]
diff --git a/backend/database/schema.sql b/backend/database/schema.sql
index fee9d73..3878364 100644
--- a/backend/database/schema.sql
+++ b/backend/database/schema.sql
@@ -63,7 +63,7 @@ CREATE TABLE application (
user_id INTEGER NOT NULL,
job_id INTEGER NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
- status_id INTEGER NOT NULL,
+ status_id INTEGER NOT NULL DEFAULT 2,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE,
FOREIGN KEY (job_id) REFERENCES job(id) ON DELETE CASCADE,
FOREIGN KEY (status_id) REFERENCES application_status(id) ON DELETE CASCADE
diff --git a/backend/seed.js b/backend/seed.js
index 8cb87dc..d0888e2 100644
--- a/backend/seed.js
+++ b/backend/seed.js
@@ -6,10 +6,10 @@ require("dotenv").config();
// Import database client
const database = require("./database/client");
const role = require("./database/data/role.json");
+const user = require("./database/data/user.json");
const company = require("./database/data/company.json");
const applicationStatus = require("./database/data/applicationStatus.json");
const job = require("./database/data/job.json");
-const user = require("./database/data/user.json");
const seed = async () => {
try {
diff --git a/backend/src/controllers/applicationControllers.js b/backend/src/controllers/applicationControllers.js
new file mode 100644
index 0000000..b64cc2a
--- /dev/null
+++ b/backend/src/controllers/applicationControllers.js
@@ -0,0 +1,118 @@
+// Import access to database tables
+const tables = require("../tables");
+
+// The B of BREAD - Browse (Read All) operation
+const browse = async (req, res, next) => {
+ try {
+ // Fetch all applications from the database
+ const applications = await tables.application.readAll();
+
+ // Respond with the applications in JSON format
+ res.status(200).json(applications);
+ } catch (err) {
+ // Pass any errors to the error-handling middleware
+ next(err);
+ }
+};
+
+// The R of BREAD - Read operation
+const read = async (req, res, next) => {
+ try {
+ // Fetch a specific application from the database based on the provided ID
+ const application = await tables.application.read(req.params.id);
+
+ // If the application is not found, respond with HTTP 404 (Not Found)
+ // Otherwise, respond with the application in JSON format
+ if (application == null) {
+ res.sendStatus(404);
+ } else {
+ res.status(200).json(application);
+ }
+ } catch (err) {
+ // Pass any errors to the error-handling middleware
+ next(err);
+ }
+};
+
+const readProfileApplications = async (req, res, next) => {
+ try {
+ const applications = await tables.application.getProfileApplications(
+ req.user.id
+ );
+
+ if (applications == null) {
+ res.sendStatus(404);
+ } else {
+ res.status(200).json(applications);
+ }
+ } catch (err) {
+ next(err);
+ }
+};
+
+const readConsultantApplications = async (req, res, next) => {
+ try {
+ const applications = await tables.application.getConsultantApplications(
+ req.user.id
+ );
+
+ if (applications == null) {
+ res.sendStatus(404);
+ } else {
+ res.status(200).json(applications);
+ }
+ } catch (err) {
+ next(err);
+ }
+};
+
+// The E of BREAD - Edit (Update) operation
+const edit = async (req, res, next) => {
+ // Extract the application data from the request body
+ const application = req.body;
+ try {
+ // Fetch a specific city from the database based on the provided ID
+ const result = await tables.application.update(req.params.id, application);
+
+ // If the application 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 application data from the request body
+ const application = req.body;
+
+ try {
+ // Insert the application into the database
+ const insertId = await tables.application.create(application);
+
+ // Respond with HTTP 201 (Created) and the ID of the newly inserted application
+ res.status(201).json({ insertId });
+ } catch (err) {
+ // Pass any errors to the error-handling middleware
+ next(err);
+ }
+};
+
+// The D of BREAD - Destroy (Delete) operation
+// This operation is not yet implemented
+
+// Ready to export the controller functions
+module.exports = {
+ browse,
+ read,
+ readProfileApplications,
+ readConsultantApplications,
+ edit,
+ add,
+ // destroy,
+};
diff --git a/backend/src/controllers/applicationStatusControllers.js b/backend/src/controllers/applicationStatusControllers.js
new file mode 100644
index 0000000..36243ab
--- /dev/null
+++ b/backend/src/controllers/applicationStatusControllers.js
@@ -0,0 +1,21 @@
+// Import access to database tables
+const tables = require("../tables");
+
+// The B of BREAD - Browse (Read All) operation
+const browse = async (req, res, next) => {
+ try {
+ // Fetch all status from the database
+ const status = await tables.application_status.readAll();
+
+ // Respond with the status in JSON format
+ res.status(200).json(status);
+ } catch (err) {
+ // Pass any errors to the error-handling middleware
+ next(err);
+ }
+};
+
+// Ready to export the controller functions
+module.exports = {
+ browse,
+};
diff --git a/backend/src/controllers/userControllers.js b/backend/src/controllers/userControllers.js
index 482f4d4..2087085 100644
--- a/backend/src/controllers/userControllers.js
+++ b/backend/src/controllers/userControllers.js
@@ -188,6 +188,10 @@ const destroy = async (req, res, next) => {
}
};
+const logout = (req, res) => {
+ res.cookie("auth", "", { expires: new Date(0) }).sendStatus(200);
+};
+
// Ready to export the controller functions
module.exports = {
getConsultant,
@@ -202,4 +206,5 @@ module.exports = {
destroy,
updateProfile,
updateProfileCV,
+ logout,
};
diff --git a/backend/src/models/ApplicationManager.js b/backend/src/models/ApplicationManager.js
new file mode 100644
index 0000000..16778c2
--- /dev/null
+++ b/backend/src/models/ApplicationManager.js
@@ -0,0 +1,111 @@
+const AbstractManager = require("./AbstractManager");
+
+class ApplicationManager extends AbstractManager {
+ constructor() {
+ // Call the constructor of the parent class (AbstractManager)
+ // and pass the table name "application" as configuration
+ super({ table: "application" });
+ }
+
+ // The C of CRUD - Create operation
+
+ async create(application) {
+ // Execute the SQL INSERT query to add a new application to the "application" table
+ const [result] = await this.database.query(
+ `insert into ${this.table} (user_id, job_id) values (?, ?)`,
+ [application.user_id, application.job_id]
+ );
+
+ // Return the ID of the newly inserted application
+ return result.insertId;
+ }
+
+ // The Rs of CRUD - Read operations
+
+ async read(id) {
+ // Execute the SQL SELECT query to retrieve a specific application by its ID
+ const [rows] = await this.database.query(
+ `select * from ${this.table} where id = ?`,
+ [id]
+ );
+
+ // Return the first row of the result, which represents the application
+ return rows[0];
+ }
+
+ async readAll() {
+ // Execute the SQL SELECT query to retrieve all applications from the "application" table
+ const [rows] = await this.database.query(`select * from ${this.table}`);
+
+ // Return the array of applications
+ return rows;
+ }
+
+ async getProfileApplications(userId) {
+ const [rows] = await this.database.query(
+ `SELECT
+ application.id,
+ application.job_id,
+ application.status_id,
+ job.title AS job_title,
+ job.consultant_id,
+ consultant.email AS consultant_email,
+ application_status.label as status_label
+ FROM ${this.table} AS application
+ INNER JOIN job ON application.job_id = job.id
+ INNER JOIN user AS consultant ON job.consultant_id = consultant.id
+ INNER JOIN user ON application.user_id = user.id
+ INNER JOIN application_status ON application.status_id = application_status.id
+ WHERE user.id = ?`,
+ [userId]
+ );
+ return rows;
+ }
+
+ async getConsultantApplications(consultantId) {
+ const [rows] = await this.database.query(
+ `SELECT
+ application.id AS application_id,
+ application.job_id,
+ application.status_id,
+ job.title AS job_title,
+ consultant.id AS consultant_id,
+ consultant.email AS consultant_email,
+ user.id AS candidate_id,
+ user.email AS candidate_email,
+ application_status.label AS status_label,
+ company.name AS company_name
+ FROM ${this.table} AS application
+ INNER JOIN job ON application.job_id = job.id
+ INNER JOIN user AS consultant ON job.consultant_id = consultant.id
+ INNER JOIN user ON application.user_id = user.id
+ INNER JOIN application_status ON application.status_id = application_status.id
+ INNER JOIN company ON job.company_id = company.id
+ WHERE consultant.id = ?
+ ORDER BY company.name`,
+ [consultantId]
+ );
+ return rows;
+ }
+
+ // The U of CRUD - Update operation
+ async update(id, application) {
+ // Execute the SQL SELECT query to retrieve a specific application by its ID
+ const [result] = await this.database.query(
+ `UPDATE ${this.table} set ? WHERE id = ?`,
+ [application, 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 application by its ID
+
+ // async delete(id) {
+ // ...
+ // }
+}
+
+module.exports = ApplicationManager;
diff --git a/backend/src/models/ApplicationStatusManager.js b/backend/src/models/ApplicationStatusManager.js
new file mode 100644
index 0000000..448d1c4
--- /dev/null
+++ b/backend/src/models/ApplicationStatusManager.js
@@ -0,0 +1,19 @@
+const AbstractManager = require("./AbstractManager");
+
+class ApplicationStatusManager extends AbstractManager {
+ constructor() {
+ // Call the constructor of the parent class (AbstractManager)
+ // and pass the table name "application_status" as configuration
+ super({ table: "application_status" });
+ }
+
+ async readAll() {
+ // Execute the SQL SELECT query to retrieve all status from the "application_status" table
+ const [rows] = await this.database.query(`select * from ${this.table}`);
+
+ // Return the array of status
+ return rows;
+ }
+}
+
+module.exports = ApplicationStatusManager;
diff --git a/backend/src/router.js b/backend/src/router.js
index fafa0ee..f479526 100644
--- a/backend/src/router.js
+++ b/backend/src/router.js
@@ -12,6 +12,8 @@ const userControllers = require("./controllers/userControllers");
const jobControllers = require("./controllers/jobControllers");
const companyControllers = require("./controllers/companyControllers");
const roleControllers = require("./controllers/roleControllers");
+const applicationControllers = require("./controllers/applicationControllers");
+const applicationStatusControllers = require("./controllers/applicationStatusControllers");
const checkCredentials = require("./middleware/checkCredentials");
const checkAdmin = require("./middleware/checkAdmin");
@@ -23,11 +25,17 @@ const validateCompany = require("./validators/validateCompany");
const validateCV = require("./validators/validateCV");
const validateJob = require("./validators/validateJob");
+// ROUTES GET
router.get("/jobs", jobControllers.browse);
router.get("/locations", jobControllers.getLocations);
router.get("/languages", jobControllers.getLanguages);
router.get("/companies", companyControllers.browse);
-router.get("/consultants", userControllers.getConsultant);
+router.get(
+ "/consultants",
+ checkCredentials,
+ checkConsultant,
+ userControllers.getConsultant
+);
router.get("/roles", checkCredentials, checkAdmin, roleControllers.browse);
router.get(
"/candidates",
@@ -36,8 +44,26 @@ router.get(
userControllers.getCandidates
);
router.get("/profile", checkCredentials, userControllers.getProfile);
+router.get(
+ "/profile/applications",
+ checkCredentials,
+ applicationControllers.readProfileApplications
+);
+router.get(
+ "/applications/consultant",
+ checkCredentials,
+ checkConsultant,
+ applicationControllers.readConsultantApplications
+);
router.get("/jobs/all/latest", jobControllers.browseLatest);
+router.get(
+ "/applicationStatus",
+ checkCredentials,
+ checkConsultant,
+ applicationStatusControllers.browse
+);
+// ROUTES GET BY ID
router.get("/jobs/:id", jobControllers.read);
router.get(
"/companies/:id",
@@ -76,7 +102,9 @@ router.post(
validateCompany,
companyControllers.add
);
+router.post("/application", checkCredentials, applicationControllers.add);
+// ROUTES DELETE
router.delete(
"/jobs/:id",
checkCredentials,
@@ -96,6 +124,7 @@ router.delete(
userControllers.destroy
);
+// ROUTES PUT
router.put(
"/companies/:id",
checkCredentials,
diff --git a/backend/src/tables.js b/backend/src/tables.js
index c283a42..24ef145 100644
--- a/backend/src/tables.js
+++ b/backend/src/tables.js
@@ -7,12 +7,16 @@ const JobManager = require("./models/JobManager");
const CompanyManager = require("./models/CompanyManager");
const UserManager = require("./models/UserManager");
const RoleManager = require("./models/RoleManager");
+const ApplicationManager = require("./models/ApplicationManager");
+const ApplicationStatusManager = require("./models/ApplicationStatusManager");
const managers = [
JobManager,
CompanyManager,
UserManager,
RoleManager,
+ ApplicationManager,
+ ApplicationStatusManager,
// Add other managers here
];
diff --git a/frontend/src/components/CarouselJobs.jsx b/frontend/src/components/CarouselJobs.jsx
index 82f8f95..b8d2155 100644
--- a/frontend/src/components/CarouselJobs.jsx
+++ b/frontend/src/components/CarouselJobs.jsx
@@ -41,7 +41,7 @@ function Carousel({ jobs }) {
>
{jobs.map((job) => (
-
+
))}
diff --git a/frontend/src/components/JobCard.jsx b/frontend/src/components/JobCard.jsx
index 4f09d97..35ef55b 100644
--- a/frontend/src/components/JobCard.jsx
+++ b/frontend/src/components/JobCard.jsx
@@ -43,22 +43,23 @@ function JobCard({ job, cardStyle, refresh, isUserPage }) {
>
{job.title}
- {access ? (
-
-
-
-
-
-
+
+ ) : (
+ ""
+ )}
+ {!isUserPage ? (
+
+ Supprimer
+
) : (
{
+ try {
+ await connexion.post("/logout");
+ logout();
+ navigate("/");
+ window.location.reload();
+ } catch (err) {
+ console.error("Erreur lors de la déconnexion :", err);
+ }
+ };
return (
@@ -43,7 +56,11 @@ function NavBar() {
)}
{connected.role_id && (
-
+
Deconnexion
)}
diff --git a/frontend/src/components/SelectConsultant.jsx b/frontend/src/components/SelectConsultant.jsx
index 9666ead..fd29880 100644
--- a/frontend/src/components/SelectConsultant.jsx
+++ b/frontend/src/components/SelectConsultant.jsx
@@ -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 (
);
@@ -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;
diff --git a/frontend/src/components/SelectFromList.jsx b/frontend/src/components/SelectFromList.jsx
index 0d26197..c549ce6 100644
--- a/frontend/src/components/SelectFromList.jsx
+++ b/frontend/src/components/SelectFromList.jsx
@@ -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 (
@@ -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;
diff --git a/frontend/src/contexts/auth.jsx b/frontend/src/contexts/auth.jsx
index 2f049d2..92a71bc 100644
--- a/frontend/src/contexts/auth.jsx
+++ b/frontend/src/contexts/auth.jsx
@@ -24,6 +24,7 @@ export function AuthProvider({ children }) {
() => ({ connected, setConnected, logout }),
[connected, setConnected]
);
+
useEffect(() => {
const getProfile = async () => {
try {
diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx
index aa5f424..452cb9a 100644
--- a/frontend/src/main.jsx
+++ b/frontend/src/main.jsx
@@ -23,6 +23,8 @@ import AdminSpecific from "./pages/AdminSpecific";
import FormCompany from "./pages/FormCompany";
import JobId from "./pages/jobId";
+import CandidatApplications from "./pages/CandidatApplications";
+import ConsultantApplication from "./pages/ConsultantApplications";
const router = createBrowserRouter([
{
@@ -81,6 +83,10 @@ const router = createBrowserRouter([
.catch((err) => console.error(err));
},
},
+ {
+ path: "applications",
+ element: ,
+ },
{
path: "administration",
children: [
@@ -116,6 +122,10 @@ const router = createBrowserRouter([
path: "/account",
element: ,
},
+ {
+ path: "/account/applications",
+ element: ,
+ },
{
path: "/administration",
element: ,
diff --git a/frontend/src/pages/AdminJobs.jsx b/frontend/src/pages/AdminJobs.jsx
index 3ffa40f..04caf1c 100644
--- a/frontend/src/pages/AdminJobs.jsx
+++ b/frontend/src/pages/AdminJobs.jsx
@@ -30,7 +30,6 @@ function AdminJobs() {
const [isSubmissionSuccessful, setIsSubmissionSuccessful] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const { id } = useParams();
-
const handleJob = (event) => {
setJob((previousState) => ({
...previousState,
@@ -94,6 +93,7 @@ function AdminJobs() {
handleSelect={handleJob}
name="company_id"
className="filter-select"
+ company={job.company_id}
/>
+
+ navigate("/account/applications")}
+ >
+ Voir mes candidatures
+
+
>
);
}
diff --git a/frontend/src/pages/CandidatApplications.css b/frontend/src/pages/CandidatApplications.css
new file mode 100644
index 0000000..27dc529
--- /dev/null
+++ b/frontend/src/pages/CandidatApplications.css
@@ -0,0 +1,38 @@
+.candidat-application {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.candidat-application-title {
+ margin-top: 1rem;
+ margin-bottom: 2rem;
+}
+
+.application-table {
+ width: 50%;
+ margin-bottom: 40rem;
+}
+
+.application-th {
+ width: auto;
+ border-bottom: 2px solid var(--main-red);
+ font-size: 1.5rem;
+}
+
+.application-td {
+ height: 3rem;
+ text-align: center;
+ border-bottom: 2px solid var(--main-red);
+ font-size: 1.1rem;
+}
+
+.job-link {
+ text-decoration: underline;
+ text-underline-position: under;
+}
+
+.job-link:hover {
+ color: var(--main-red);
+}
diff --git a/frontend/src/pages/CandidatApplications.jsx b/frontend/src/pages/CandidatApplications.jsx
new file mode 100644
index 0000000..1279b11
--- /dev/null
+++ b/frontend/src/pages/CandidatApplications.jsx
@@ -0,0 +1,64 @@
+import React, { useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import { useAuthContext } from "../contexts/auth";
+import connexion from "../services/connexion";
+
+import "./CandidatApplications.css";
+
+function CandidatApplications() {
+ const { connected } = useAuthContext();
+ const [applications, setApplications] = useState([]);
+
+ const getApplications = async () => {
+ try {
+ const response = await connexion.get(
+ "/profile/applications",
+ connected.id
+ );
+ setApplications(response.data);
+ } catch (err) {
+ console.error(err);
+ throw err;
+ }
+ };
+
+ useEffect(() => {
+ getApplications();
+ }, []);
+
+ return (
+
+
Mes candidatures
+ {applications.length > 0 ? (
+
+
+
+ Nom de l'offre |
+ Email du consultant |
+ Statut |
+
+
+
+ {applications.map((application) => (
+
+
+
+ {application.job_title}
+
+ |
+
+ {application.consultant_email}
+ |
+ {application.status_label} |
+
+ ))}
+
+
+ ) : (
+
Vous n'avez postulé à aucune offre pour le moment
+ )}
+
+ );
+}
+
+export default CandidatApplications;
diff --git a/frontend/src/pages/ConsultantApplications.css b/frontend/src/pages/ConsultantApplications.css
new file mode 100644
index 0000000..2c74906
--- /dev/null
+++ b/frontend/src/pages/ConsultantApplications.css
@@ -0,0 +1,51 @@
+.consultant-application {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+}
+
+.consultant-application-title {
+ margin-top: 1rem;
+ margin-bottom: 2rem;
+}
+
+.application-table {
+ width: 50%;
+ margin-bottom: 40rem;
+}
+
+.application-th {
+ width: auto;
+ border-bottom: 2px solid var(--main-red);
+ font-size: 1.5rem;
+}
+
+.application-td {
+ height: 3rem;
+ text-align: center;
+ border-bottom: 2px solid var(--main-red);
+ font-size: 1.1rem;
+}
+
+.job-link {
+ text-decoration: underline;
+ text-underline-position: under;
+}
+
+.job-link:hover {
+ color: var(--main-red);
+}
+
+.application-status {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-around;
+ align-items: center;
+}
+
+.validate-status-button {
+ width: 5rem;
+ height: 2rem;
+}
diff --git a/frontend/src/pages/ConsultantApplications.jsx b/frontend/src/pages/ConsultantApplications.jsx
new file mode 100644
index 0000000..4323825
--- /dev/null
+++ b/frontend/src/pages/ConsultantApplications.jsx
@@ -0,0 +1,156 @@
+import React, { useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import Select from "react-select";
+
+import { useAuthContext } from "../contexts/auth";
+import connexion from "../services/connexion";
+
+import "./ConsultantApplications.css";
+import "../components/reusable/button.css";
+
+function ConsultantApplication() {
+ const { connected } = useAuthContext();
+ const [applications, setApplications] = useState([]);
+ const [status, setStatus] = useState([]);
+ const [selectedStatuses, setSelectedStatuses] = useState([]);
+
+ const colorStyles = {
+ control: (styles, { isSelected, isFocused }) => ({
+ ...styles,
+ backgroundColor: "white",
+ borderColor: isSelected || isFocused ? "#ca2061" : "black",
+ boxShadow: "none",
+ ":hover": { borderColor: "#ca2061" },
+ }),
+ clearIndicator: (styles) => ({
+ ...styles,
+ cursor: "pointer",
+ ":hover": { color: "red" },
+ }),
+ dropdownIndicator: (styles) => ({
+ ...styles,
+ cursor: "pointer",
+ }),
+ option: (styles, { isFocused }) => {
+ return {
+ ...styles,
+ backgroundColor: isFocused ? "#ca2061" : "white",
+ color: isFocused ? "white" : "black",
+ cursor: isFocused ? "pointer" : "default",
+ };
+ },
+ };
+
+ const getApplications = async () => {
+ try {
+ const response = await connexion.get(
+ "/applications/consultant",
+ connected.id
+ );
+ setApplications(response.data);
+
+ const initialSelectedStatuses = response.data.map((application) => {
+ return {
+ id: application.status_id,
+ label: application.status_label,
+ };
+ });
+ setSelectedStatuses(initialSelectedStatuses);
+ } catch (err) {
+ console.error(err);
+ throw err;
+ }
+ };
+
+ const getApplicationStatus = async () => {
+ try {
+ const response = await connexion.get("/applicationStatus");
+ setStatus(response.data);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const handleStatusChange = (index, selectedOption) => {
+ const newSelectedStatuses = [...selectedStatuses];
+ newSelectedStatuses[index] = selectedOption;
+ setSelectedStatuses(newSelectedStatuses);
+ };
+
+ const validateStatusChange = async (applicationId, selectedStatusId) => {
+ try {
+ await connexion.put(`application/${applicationId}`, {
+ status_id: selectedStatusId,
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ useEffect(() => {
+ getApplications();
+ getApplicationStatus();
+ }, []);
+
+ return (
+
+
Mes candidatures
+ {applications.length > 0 ? (
+
+
+
+ Nom de l'offre |
+ Entreprise |
+ Email du candidat |
+ Statut |
+
+
+
+ {applications.map((application, index) => (
+
+
+
+ {application.job_title}
+
+ |
+ {application.company_name} |
+
+ {application.candidate_email}
+ |
+
+ |
+
+ ))}
+
+
+ ) : (
+
Aucun candidat n'a postulé à vos offres pour le moment
+ )}
+
+ );
+}
+
+export default ConsultantApplication;
diff --git a/frontend/src/pages/FormLogin.jsx b/frontend/src/pages/FormLogin.jsx
index a46d15e..f4e663e 100644
--- a/frontend/src/pages/FormLogin.jsx
+++ b/frontend/src/pages/FormLogin.jsx
@@ -30,7 +30,6 @@ function FormLogin() {
try {
const valid = await connexion.post("/login", credentials);
setConnected(valid.data);
- sessionStorage.setItem("connected", true);
setTimeout(() => {
navigate("/");
}, 1000);
diff --git a/frontend/src/pages/HomePage.css b/frontend/src/pages/HomePage.css
index 65af8c6..cc421c0 100644
--- a/frontend/src/pages/HomePage.css
+++ b/frontend/src/pages/HomePage.css
@@ -121,7 +121,7 @@
justify-content: center;
align-items: center;
height: 20rem;
- background-image: url("../public/sfddsfdfsfddf.png");
+ background-image: url("../assets/sfddsfdfsfddf.png");
background-position: center;
background-size: contain;
background-repeat: no-repeat;
diff --git a/frontend/src/pages/consultant/ConsultantJob.css b/frontend/src/pages/consultant/ConsultantJob.css
index ddb9f03..4e45e1a 100644
--- a/frontend/src/pages/consultant/ConsultantJob.css
+++ b/frontend/src/pages/consultant/ConsultantJob.css
@@ -10,3 +10,8 @@
.ajout-card {
margin: auto;
}
+
+.container-ajout {
+ display: flex;
+ justify-content: center;
+}
diff --git a/frontend/src/pages/consultant/ConsultantJob.jsx b/frontend/src/pages/consultant/ConsultantJob.jsx
index aec81c6..d42ad75 100644
--- a/frontend/src/pages/consultant/ConsultantJob.jsx
+++ b/frontend/src/pages/consultant/ConsultantJob.jsx
@@ -20,15 +20,16 @@ function ConsultantJob() {
useEffect(() => {
getJobsByCompany();
}, []);
-
return (
-
-
-
- Ajouter un job
-
-
+
+
+
+
+ Ajouter un job
+
+
+
{jobs.map((job) => (
diff --git a/frontend/src/pages/jobId.jsx b/frontend/src/pages/jobId.jsx
index a8c7701..c1f4853 100644
--- a/frontend/src/pages/jobId.jsx
+++ b/frontend/src/pages/jobId.jsx
@@ -1,8 +1,10 @@
-import React, { useRef } from "react";
+import React, { useRef, useContext } from "react";
import { useLoaderData } from "react-router-dom";
import Modal from "../components/modal";
import HeadJob from "../components/headJobs";
+import connexion from "../services/connexion";
+import { AuthContext } from "../contexts/auth";
import "./jobId.css";
import "../components/headJobs.css";
@@ -13,12 +15,23 @@ function formatDate(time) {
function JobId() {
const job = useLoaderData();
+ const { connected } = useContext(AuthContext);
const modal = useRef(null);
function toggleRefModal() {
modal.current.toggleModal();
}
+ const handleClick = async () => {
+ toggleRefModal();
+ const application = { job_id: job.id, user_id: connected.id };
+ try {
+ connexion.post("/application", application);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
return (
@@ -51,7 +64,7 @@ function JobId() {
Veuillez vous connecter pour en savoir plus
-
+
Postuler a l'offre
diff --git a/frontend/src/pages/layout/ConsultantPage.css b/frontend/src/pages/layout/ConsultantPage.css
index ed271e6..cae2f9f 100644
--- a/frontend/src/pages/layout/ConsultantPage.css
+++ b/frontend/src/pages/layout/ConsultantPage.css
@@ -11,3 +11,8 @@
font-weight: bolder;
text-align: center;
}
+
+.application-button-container {
+ display: flex;
+ justify-content: center;
+}
diff --git a/frontend/src/pages/layout/ConsultantPage.jsx b/frontend/src/pages/layout/ConsultantPage.jsx
index c08c148..6326547 100644
--- a/frontend/src/pages/layout/ConsultantPage.jsx
+++ b/frontend/src/pages/layout/ConsultantPage.jsx
@@ -1,10 +1,21 @@
-import { Outlet } from "react-router-dom";
+import { Outlet, useNavigate } from "react-router-dom";
import "./ConsultantPage.css";
function ConsultantPage() {
+ const navigate = useNavigate();
return (
Consultant
+
+ navigate("/consultants/applications")}
+ >
+ Voir mes candidatures
+
+
+