├── Client ├── src │ ├── index.css │ ├── assets │ │ ├── icons │ │ │ ├── error.png │ │ │ ├── teamIcon.png │ │ │ ├── profileIcon.png │ │ │ ├── templeteIcon.png │ │ │ ├── careerPageIcon.png │ │ │ ├── home.svg │ │ │ ├── down.svg │ │ │ ├── reports.svg │ │ │ ├── show_more.svg │ │ │ ├── asd.svg │ │ │ ├── jobs.svg │ │ │ ├── delete.svg │ │ │ ├── employees.svg │ │ │ ├── settings.svg │ │ │ ├── notification.svg │ │ │ ├── candidates.svg │ │ │ └── share.svg │ │ └── illustrations │ │ │ ├── 404.png │ │ │ ├── job.png │ │ │ ├── dep_1.jpg │ │ │ ├── dep_2.jpg │ │ │ ├── dep_3.jpg │ │ │ └── done.png │ ├── Components │ │ ├── Common │ │ │ ├── ProtectedRoute.jsx │ │ │ ├── MainButton.jsx │ │ │ ├── InputText.jsx │ │ │ └── GoBackButton.jsx │ │ ├── ProfileSetup │ │ │ ├── ProfileSetup.jsx │ │ │ ├── SucessModel.jsx │ │ │ ├── ProfileP1.jsx │ │ │ └── Profile_Social3.jsx │ │ ├── Dashboard │ │ │ ├── ProfileCreation │ │ │ │ └── NavigationTab.jsx │ │ │ └── CreateJob │ │ │ │ ├── TopRcruitementCycle.jsx │ │ │ │ ├── CreatedJobElement.jsx │ │ │ │ ├── AppliedApplicantProfile.jsx │ │ │ │ ├── CreateJobHeadaer.jsx │ │ │ │ └── FIlterProfiles.jsx │ │ ├── HiredCandidatePage │ │ │ └── MainPage.jsx │ │ └── RecruitmentStage │ │ │ ├── DeleteCandidateProfileButton.jsx │ │ │ ├── SwitchStatus.jsx │ │ │ ├── ReccomendidCandidateCard.jsx │ │ │ ├── WithdrawnDetailsCard.jsx │ │ │ ├── WithdrawnCandidateCard.jsx │ │ │ └── ReccomendedCandidateDetailsCard.jsx │ ├── Pages │ │ ├── Dashboard │ │ │ ├── ProfileCreation │ │ │ │ ├── Profile-Office.jsx │ │ │ │ ├── Profile-Sucess.jsx │ │ │ │ ├── ProfileSocial.jsx │ │ │ │ ├── ProfileTeam_Members.jsx │ │ │ │ └── ProfileCreation.jsx │ │ │ └── NotPageFound404.jsx │ │ ├── HiredCandidates │ │ │ ├── HiredCandidate.jsx │ │ │ └── HiredCandidatesDetails.jsx │ │ ├── RecruitmentCycle │ │ │ ├── RejectedCandidates.jsx │ │ │ ├── InterviewingCandidate.jsx │ │ │ ├── WithdrawnCandidate.jsx │ │ │ ├── HiredCandidates.jsx │ │ │ ├── ReccomendedCandidates.jsx │ │ │ ├── AppliedCandidateDetails.jsx │ │ │ ├── WithdrawnDetails.jsx │ │ │ └── ReccomendedCandidatesDetails.jsx │ │ ├── Employees │ │ │ ├── MainPageOfEmployees.jsx │ │ │ └── AddNewEmployee.jsx │ │ ├── CreateJob │ │ │ └── CreateJob.jsx │ │ ├── Settings │ │ │ └── MainPageOfSetting.jsx │ │ ├── EndUser │ │ │ ├── PostedJobDescription.jsx │ │ │ └── postedJobs.jsx │ │ └── Auth │ │ │ └── ForgetPassword.jsx │ ├── App │ │ └── Store.js │ ├── main.jsx │ ├── Features │ │ ├── Dashboard │ │ │ └── Organization_Details_Slice.js │ │ └── JobCycle │ │ │ └── storeAllCandidatesDetails.js │ └── App.css ├── public │ └── logo.png ├── postcss.config.cjs ├── vite.config.js ├── .gitignore ├── tailwind.config.cjs ├── package.json └── index.html ├── Server ├── Routes │ ├── Report.js │ ├── SettingRouter.js │ ├── ProfileCreation.js │ ├── UserRoute.js │ ├── Jobs.js │ └── RecruitmentCycle.js ├── Controllers │ ├── Recruitment Cycle │ │ ├── HiredEmail.js │ │ ├── GetCandidateDetails.js │ │ ├── ShowActiveCandidateDetails.js │ │ ├── ShowInterviewingCandidate.js │ │ ├── GetReccomendedCandidateDetails.js │ │ ├── GetWithdrawnCandidateDetails.js │ │ ├── GetComments.js │ │ ├── GetHiredCandidate.js │ │ ├── GetRejectedCandidate.js │ │ ├── UpdateWithdrawnReason.js │ │ ├── GetWithdrawnCandidate.js │ │ ├── SaveInterviewDateAndTime.js │ │ ├── HandleComments.js │ │ ├── GetReccomendedCandidates.js │ │ ├── PatchComments.js │ │ ├── DeleteCandidateProfile.js │ │ ├── UpdateStatus.js │ │ ├── SubmitFeedbacl.js │ │ ├── SendInterviewEmail.js │ │ ├── SentHiredEmail.js │ │ ├── SentDeclinedEmail.js │ │ └── FilterCandidates.js │ ├── Jobs │ │ ├── GetAllPostedJobs.js │ │ ├── GetSelectedJobDescription.js │ │ ├── FilterShowClosedJobs.js │ │ ├── Filter-ShowActiveJobs.js │ │ ├── GetJobs.js │ │ ├── GetOrganizationPostedJob.js │ │ └── PostJob.js │ ├── Dashboard │ │ ├── GetProfilePic.js │ │ └── Home.js │ ├── Settings │ │ ├── UpdateProfileSettings.js │ │ └── UpdateProfilePicture.js │ ├── UserController │ │ ├── VerifyMail.js │ │ ├── UpdatePassword.js │ │ ├── verifyForgetpwd.js │ │ └── Login.js │ ├── Setup Profile │ │ └── Profile_Setup.js │ └── Report Stats │ │ └── MainPage.js ├── vercel.json ├── Config │ ├── Cloudnary.js │ ├── Database.js │ └── Multer.js ├── .gitignore ├── Middleware │ ├── VerifyToken.js │ └── AuthMiddleware.js ├── Models │ ├── Comment.js │ ├── User_Model.js │ ├── Organization_Model.js │ ├── JobModel.js │ └── Candidate.js ├── package.json └── index.js ├── .gitignore └── README.md /Client/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /Client/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/public/logo.png -------------------------------------------------------------------------------- /Client/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /Client/src/assets/icons/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/icons/error.png -------------------------------------------------------------------------------- /Client/src/assets/icons/teamIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/icons/teamIcon.png -------------------------------------------------------------------------------- /Client/src/assets/icons/profileIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/icons/profileIcon.png -------------------------------------------------------------------------------- /Client/src/assets/illustrations/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/illustrations/404.png -------------------------------------------------------------------------------- /Client/src/assets/illustrations/job.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/illustrations/job.png -------------------------------------------------------------------------------- /Client/src/assets/icons/templeteIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/icons/templeteIcon.png -------------------------------------------------------------------------------- /Client/src/assets/illustrations/dep_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/illustrations/dep_1.jpg -------------------------------------------------------------------------------- /Client/src/assets/illustrations/dep_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/illustrations/dep_2.jpg -------------------------------------------------------------------------------- /Client/src/assets/illustrations/dep_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/illustrations/dep_3.jpg -------------------------------------------------------------------------------- /Client/src/assets/illustrations/done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/illustrations/done.png -------------------------------------------------------------------------------- /Client/src/assets/icons/careerPageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hamza-Sajid/Smart-Cruiter-FYP/HEAD/Client/src/assets/icons/careerPageIcon.png -------------------------------------------------------------------------------- /Client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /Server/Routes/Report.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const StatMainPage = require('../Controllers/Report Stats/MainPage'); 3 | 4 | const RouterReport = express.Router(); 5 | 6 | RouterReport.post("/main", StatMainPage) 7 | 8 | module.exports = RouterReport; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/HiredEmail.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | 4 | 5 | const HiredEmail = async (req, res, next) => { 6 | 7 | const { email } = req.body; 8 | 9 | 10 | 11 | } 12 | 13 | module.exports = HiredEmail; -------------------------------------------------------------------------------- /Server/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "index.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "index.js" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /Server/Config/Cloudnary.js: -------------------------------------------------------------------------------- 1 | const Cloudinary = require('cloudinary'); 2 | 3 | const cloud = Cloudinary.config({ 4 | 5 | cloud_name: process.env.CLOUDNAME, 6 | api_key: process.env.APIKEY, 7 | api_secret: process.env.APISECRET 8 | 9 | }); 10 | 11 | 12 | module.exports = cloud; -------------------------------------------------------------------------------- /Client/src/Components/Common/ProtectedRoute.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Navigate, Outlet } from "react-router-dom"; 3 | 4 | function ProtectedRoute() { 5 | const isLogin = localStorage.getItem("token"); 6 | return isLogin ? : ; 7 | } 8 | 9 | export default ProtectedRoute; 10 | -------------------------------------------------------------------------------- /Client/src/Components/Common/MainButton.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function MainButton({ value }) { 4 | return ( 5 | 11 | ); 12 | } 13 | 14 | export default MainButton; 15 | -------------------------------------------------------------------------------- /Server/Config/Database.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose") 2 | const connection = async () => { 3 | try { 4 | const connect = await mongoose.connect(process.env.MONGOURL); 5 | console.log("Database connected"); 6 | } 7 | 8 | catch (e) { 9 | console.log("DB Error: " + e); 10 | } 11 | } 12 | 13 | module.exports = connection; -------------------------------------------------------------------------------- /Client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | 12 | node_modules 13 | dist 14 | dist-ssr 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /Server/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .env 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | pnpm-debug.log* 10 | lerna-debug.log* 11 | .env 12 | node_modules 13 | dist 14 | dist-ssr 15 | *.local 16 | 17 | # Editor directories and files 18 | .vscode/* 19 | !.vscode/extensions.json 20 | .idea 21 | .DS_Store 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw? 27 | -------------------------------------------------------------------------------- /Server/Config/Multer.js: -------------------------------------------------------------------------------- 1 | 2 | const multer = require('multer') 3 | 4 | const data = multer({ 5 | storage: multer.diskStorage({}), 6 | fileFilter: (req, file, cb) => { 7 | if (!file.mimetype.match(/jpg|jpeg|png/)) { 8 | cb(new Error("file is not in supported format"), false); 9 | return 10 | } 11 | cb(null, true) 12 | } 13 | }) 14 | 15 | module.exports = data; -------------------------------------------------------------------------------- /Client/src/Pages/Dashboard/ProfileCreation/Profile-Office.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Profile_Office2 from "../../../Components/ProfileSetup/Profile_Office2"; 3 | export default function Profile_Office() { 4 | return ( 5 |
6 |
7 |

Profile Creation

8 | 9 |
10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /Client/src/Pages/Dashboard/ProfileCreation/Profile-Sucess.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import SucessModel from "../../../Components/ProfileSetup/SucessModel"; 3 | 4 | function Profile_Sucess() { 5 | return ( 6 |
7 |
8 |

Profile Creation

9 | 10 |
11 |
12 | ); 13 | } 14 | 15 | export default Profile_Sucess; 16 | -------------------------------------------------------------------------------- /Client/src/Pages/Dashboard/ProfileCreation/ProfileSocial.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Profile_Social3 from "../../../Components/ProfileSetup/Profile_Social3"; 3 | 4 | function ProfileSocial() { 5 | return ( 6 |
7 |
8 |

Profile Creation

9 | 10 |
11 |
12 | ); 13 | } 14 | 15 | export default ProfileSocial; 16 | -------------------------------------------------------------------------------- /Client/src/assets/icons/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Client/src/Components/Common/InputText.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | function InputText({ type, palceholder, value, label, onChange }) { 4 | return ( 5 | <> 6 | 7 | 14 | 15 | ); 16 | } 17 | 18 | export default InputText; 19 | -------------------------------------------------------------------------------- /Client/src/Pages/Dashboard/ProfileCreation/ProfileTeam_Members.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ProfileAddTeam from "../../../Components/ProfileSetup/ProfileAddTeam"; 3 | 4 | function ProfileTeam_Members() { 5 | return ( 6 |
7 |
8 |

Profile Creation

9 | 10 |
11 |
12 | ); 13 | } 14 | 15 | export default ProfileTeam_Members; 16 | -------------------------------------------------------------------------------- /Client/src/App/Store.js: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit' 2 | import OrganizationDetailsReducer from '../Features/Dashboard/Organization_Details_Slice' 3 | import OrganizationDetails from '../Features/JobCycle/storeAllCandidatesDetails' 4 | export const store = configureStore({ 5 | reducer: { OrganizationDetailsReducer, } 6 | 7 | // reducer: { 8 | 9 | 10 | // organizationReducer: OrganizationDetailsReducer, 11 | // candidatesReducer: OrganizationDetails, 12 | 13 | // } 14 | }) -------------------------------------------------------------------------------- /Server/Middleware/VerifyToken.js: -------------------------------------------------------------------------------- 1 | const userModel = require("../Models/User_Model"); 2 | 3 | const VerifyToken = async (req, res, next) => { 4 | 5 | 6 | try { 7 | const findUser = await userModel.findById({ _id: req.body.userID }) 8 | if (findUser) { 9 | res.send(findUser) 10 | } 11 | else { 12 | res.send("INVALID TOKEN"); 13 | } 14 | } catch (error) { 15 | console.log(error); 16 | } 17 | 18 | 19 | } 20 | 21 | module.exports = VerifyToken; -------------------------------------------------------------------------------- /Server/Controllers/Jobs/GetAllPostedJobs.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Job = require('../../Models/JobModel'); 3 | 4 | const app = express(); 5 | 6 | 7 | const GetAllPostedJobs = async (req, res, next) => { 8 | 9 | const fetchAllPostedJobs = await Job.find(); 10 | 11 | if (fetchAllPostedJobs) { 12 | return res.status(200).json({ fetchAllPostedJobs }) 13 | } 14 | else { 15 | return res.status(400).json({ message: "No job found" }) 16 | } 17 | } 18 | 19 | module.exports = GetAllPostedJobs; -------------------------------------------------------------------------------- /Client/src/assets/icons/down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Client/src/assets/icons/reports.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Client/src/Pages/Dashboard/NotPageFound404.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import NotFound from "../../assets/illustrations/404.png"; 3 | function NotPageFound404() { 4 | return ( 5 |
6 |
7 | 8 |
9 |
10 |

Enter valid address!

11 |
12 |
13 | ); 14 | } 15 | 16 | export default NotPageFound404; 17 | -------------------------------------------------------------------------------- /Server/Controllers/Jobs/GetSelectedJobDescription.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Job = require("../../Models/JobModel"); 3 | const app = express(); 4 | 5 | 6 | const GetSelectedJobDescription = async (req, res, next) => { 7 | 8 | const { id } = req.body; 9 | 10 | const jobs = await Job.find({ _id: id }); 11 | if (jobs) { 12 | return res.status(200).json({ jobs }) 13 | } 14 | else { 15 | return res.status(400).json({ message: 'an error has been occured' }) 16 | } 17 | 18 | } 19 | 20 | module.exports = GetSelectedJobDescription; -------------------------------------------------------------------------------- /Server/Controllers/Jobs/FilterShowClosedJobs.js: -------------------------------------------------------------------------------- 1 | const Job = require("../../Models/JobModel"); 2 | 3 | const FilterShowClosedJobs = async (req, res) => { 4 | const id = req.body; 5 | 6 | if (!id) { 7 | return res.status(440).json({ message: "No id found" }); 8 | } 9 | 10 | const jobs = await Job.find({ org_id: id.id, job_status: 'Closed' }); 11 | 12 | if (jobs) { 13 | return res.status(200).json({ jobs }) 14 | } 15 | else { 16 | return res.status(404).json({ message: "No user found" }) 17 | } 18 | } 19 | module.exports = FilterShowClosedJobs; -------------------------------------------------------------------------------- /Client/src/Pages/Dashboard/ProfileCreation/ProfileCreation.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Link, Outlet } from "react-router-dom"; 3 | import NavigationTab from "../../../Components/Dashboard/ProfileCreation/NavigationTab"; 4 | import ProfileP1 from "../../../Components/ProfileSetup/ProfileP1"; 5 | 6 | function ProfileCreation() { 7 | return ( 8 |
9 |

Profile Creation

10 | 11 |
12 | ); 13 | } 14 | 15 | export default ProfileCreation; 16 | -------------------------------------------------------------------------------- /Server/Controllers/Jobs/Filter-ShowActiveJobs.js: -------------------------------------------------------------------------------- 1 | const Job = require("../../Models/JobModel"); 2 | 3 | const showActiveJobs = async (req, res, next) => { 4 | 5 | const id = req.body; 6 | 7 | if (!id) { 8 | return res.status(440).json({ message: "No id found" }); 9 | } 10 | 11 | const jobs = await Job.find({ org_id: id.id, job_status: 'Active' }); 12 | 13 | if (jobs) { 14 | return res.status(200).json({ jobs }) 15 | } 16 | else { 17 | return res.status(404).json({ message: "No user found" }) 18 | } 19 | 20 | } 21 | 22 | module.exports = showActiveJobs; -------------------------------------------------------------------------------- /Server/Controllers/Dashboard/GetProfilePic.js: -------------------------------------------------------------------------------- 1 | const OrganizationModal = require("../../Models/Organization_Model"); 2 | 3 | const GetProfilePicture = async (req, res) => { 4 | 5 | const organization_id = req.body.organization_id 6 | 7 | const organizaion = await OrganizationModal.findById(organization_id); 8 | if (organizaion) { 9 | const { logo } = organizaion; 10 | return res.status(200).json(logo) 11 | } 12 | else { 13 | res.status(404).json({ message: "No organization found , enter valid organization" }); 14 | } 15 | } 16 | 17 | module.exports = GetProfilePicture; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetCandidateDetails.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | const app = express(); 4 | 5 | const GetCandidateDetails = async (req, res, next) => { 6 | const { id } = req.body; 7 | 8 | const candidate_details = await Candidate.findById(id); 9 | 10 | if (candidate_details) { 11 | return res.status(200).json({ candidate_details }) 12 | } 13 | 14 | else { 15 | return res.status(404).json({ message: "No candidate found with this id" }) 16 | } 17 | } 18 | 19 | module.exports = GetCandidateDetails; -------------------------------------------------------------------------------- /Client/tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./index.html", 5 | "./src/**/*.{js,ts,jsx,tsx}", 6 | ], 7 | plugins: [require("daisyui")], 8 | theme: { 9 | extend: { 10 | colors: { 11 | 'primarytext': '333232', 12 | 'primary': '#0063B2', 13 | 'lighttext': '#C3C3C4', 14 | 'secondrytext': '#888888', 15 | 'secondry': '#2687F0', 16 | 'background': '#FAFAFC', 17 | 18 | } 19 | 20 | }, 21 | daisyui: { 22 | // themes: ["light", "dark", "cupcake"], 23 | themes: false, 24 | }, 25 | } 26 | } -------------------------------------------------------------------------------- /Server/Controllers/Jobs/GetJobs.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Job = require("../../Models/JobModel"); 3 | const app = express(); 4 | 5 | 6 | const GetJob = async (req, res, next) => { 7 | 8 | const { id } = req.body; 9 | console.log(id); 10 | if (!id) { 11 | return res.status(440).json({ message: "on id found" }); 12 | } 13 | const jobs = await Job.find({ org_id: id }); 14 | if (jobs) { 15 | return res.status(200).json({ jobs }) 16 | } 17 | else { 18 | return res.status(400).json({ message: 'an error has been occured' }) 19 | } 20 | 21 | } 22 | 23 | module.exports = GetJob; -------------------------------------------------------------------------------- /Client/src/assets/icons/show_more.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Server/Controllers/Dashboard/Home.js: -------------------------------------------------------------------------------- 1 | const OrganizationModal = require("../../Models/Organization_Model"); 2 | 3 | const Home = async (req, res, next) => { 4 | console.log(req.body) 5 | const { organization_id } = req.body.data; 6 | console.log(' I am going to run'); 7 | console.log(organization_id); 8 | const organizaion = await OrganizationModal.findById(organization_id); 9 | console.log(organizaion); 10 | if (organizaion) { 11 | return res.status(200).json({ organizaion }) 12 | } 13 | else { 14 | res.status(404).json({ message: "No organization found , enter valid organization" }); 15 | } 16 | } 17 | 18 | module.exports = Home; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/ShowActiveCandidateDetails.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | const Candidate = require('../../Models/Candidate.js') 4 | 5 | const showActiveCandidateDetails = async (req, res, next) => { 6 | const { user_id } = req.body; 7 | if (!user_id) { 8 | return res.status(400).json({ message: "Enter valid user_id" }) 9 | } 10 | const findUser = await Candidate.findById(user_id); 11 | if (findUser) { 12 | return res.status(200).json(findUser) 13 | } 14 | else { 15 | return res.status(500).json({ message: "Invalid User ID" }) 16 | 17 | } 18 | } 19 | 20 | module.exports = showActiveCandidateDetails; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/ShowInterviewingCandidate.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | 4 | const ShowInterviewingCandidate = async (req, res, next) => { 5 | 6 | 7 | const { id } = req.body.id; 8 | 9 | const findInterviewingCandidates = await Candidate.find({ 10 | recruitmentCycle: 'Interviewing', 11 | jobID: id 12 | },) 13 | 14 | if (findInterviewingCandidates) { 15 | 16 | res.status(200).json(findInterviewingCandidates) 17 | 18 | } 19 | else { 20 | 21 | res.status(400).json({ message: "No candidate found" }) 22 | } 23 | } 24 | 25 | module.exports = ShowInterviewingCandidate; -------------------------------------------------------------------------------- /Client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import { ChakraProvider } from "@chakra-ui/react"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import { store } from "./App/Store"; 7 | import { Provider } from "react-redux"; 8 | import { Analytics } from "@vercel/analytics/react"; 9 | 10 | import "./index.css"; 11 | 12 | ReactDOM.createRoot(document.getElementById("root")).render( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetReccomendedCandidateDetails.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | const app = express(); 4 | 5 | const GetReccomendedCandidatesDetails = async (req, res, next) => { 6 | const { id } = req.body; 7 | try { 8 | const getUser = await Candidate.findById(id); 9 | if (getUser) { 10 | return res.status(200).json(getUser); 11 | } else { 12 | return res.status(404).json({ message: "No user found" }); 13 | } 14 | } catch (error) { 15 | return res.status(500).json({ message: "Something unexpected happend" }); 16 | } 17 | }; 18 | 19 | module.exports = GetReccomendedCandidatesDetails; 20 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetWithdrawnCandidateDetails.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | const app = express(); 4 | const GetWithdrawnCandidateDetails = async (req, res, next) => { 5 | 6 | 7 | const { id } = req.body; 8 | 9 | try { 10 | const getUser = await Candidate.findById(id); 11 | 12 | if (getUser) { 13 | return res.status(200).json(getUser); 14 | } else { 15 | return res.status(404).json({ message: "No user found" }); 16 | } 17 | } catch (error) { 18 | return res.status(500).json({ message: "Something unexpected happend" }); 19 | } 20 | 21 | } 22 | 23 | module.exports = GetWithdrawnCandidateDetails; -------------------------------------------------------------------------------- /Server/Controllers/Settings/UpdateProfileSettings.js: -------------------------------------------------------------------------------- 1 | const { find } = require("../../Models/Organization_Model"); 2 | const OrganizationModal = require("../../Models/Organization_Model"); 3 | 4 | const UpdateProfileSettings = async (req, res) => { 5 | console.log("i am running") 6 | console.log(req.body.org_id) 7 | 8 | 9 | const { org_id } = req.body; 10 | const { inputValue } = req.body; 11 | // console.log(inputValue) 12 | const findOrganization = await OrganizationModal.findById(org_id); 13 | 14 | // if (findOrganization) { 15 | 16 | 17 | // } 18 | 19 | // else { 20 | // res.status(404).json({ message: "Organization is not found" }) 21 | // } 22 | 23 | 24 | } 25 | 26 | module.exports = UpdateProfileSettings; -------------------------------------------------------------------------------- /Server/Controllers/Jobs/GetOrganizationPostedJob.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | const app = express(); 4 | 5 | 6 | const GetOrganizationPostedJobApplicants = async (req, res, next) => { 7 | 8 | 9 | const { job_id } = req.body; 10 | if (!job_id) { 11 | return res.status(404).json({ message: "ID not found" }); 12 | } 13 | const findApplicants = await Candidate.find( 14 | { 15 | jobID: job_id, 16 | }) 17 | 18 | if (findApplicants) { 19 | return res.status(200).json(findApplicants) 20 | } 21 | else return res.status(400).json({ message: "nothing found" }); 22 | } 23 | 24 | 25 | module.exports = GetOrganizationPostedJobApplicants; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetComments.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const CandidateRemarks = require('../../Models/Comment.js'); 3 | const GetComments = async (req, res, next) => { 4 | 5 | const { id } = req.body; 6 | 7 | 8 | // Use the `findOne()` method to find a candidate with the given `user_id` 9 | CandidateRemarks.findOne({ CandidateID: id }, (err, candidate) => { 10 | if (err) { 11 | return res.status(400).json({ message: "No candidate found " }) 12 | } 13 | 14 | if (!candidate) { 15 | return res.status(500).json({ message: "Server error" }) 16 | 17 | } 18 | 19 | return res.status(200).json(candidate) 20 | }); 21 | 22 | } 23 | module.exports = GetComments; -------------------------------------------------------------------------------- /Client/src/Pages/HiredCandidates/HiredCandidate.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 3 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 4 | import MainPage from "../../Components/HiredCandidatePage/MainPage"; 5 | 6 | function HiredCandidate() { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 | 17 | 18 |
19 |
20 | ); 21 | } 22 | 23 | export default HiredCandidate; 24 | -------------------------------------------------------------------------------- /Server/Models/Comment.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const CandidateComments = new mongoose.Schema({ 4 | 5 | 6 | CandidateID: { 7 | type: String, 8 | required: true 9 | }, 10 | Applied: { 11 | type: String, 12 | }, 13 | Interviewing: { 14 | type: String, 15 | }, 16 | Reccomended: { 17 | type: String, 18 | }, 19 | Applied: { 20 | type: String, 21 | }, 22 | Hired: { 23 | type: String, 24 | }, 25 | Rejected: { 26 | type: String, 27 | }, 28 | Initialized: { 29 | type: Boolean, 30 | default: false, 31 | } 32 | }) 33 | 34 | const CandidateRemarks = mongoose.model("Comment", CandidateComments) 35 | 36 | module.exports = CandidateRemarks; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetHiredCandidate.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | const app = express(); 4 | 5 | const GetHiredCandidate = async (req, res, next) => { 6 | const { id } = req.body; 7 | 8 | try { 9 | const getUser = await Candidate.find( 10 | { 11 | jobID: id, 12 | recruitmentCycle: 'Hired' 13 | } 14 | ); 15 | if (getUser) { 16 | return res.status(200).json(getUser); 17 | } else { 18 | return res.status(404).json({ message: "No user found" }); 19 | } 20 | } catch (error) { 21 | return res.status(500).json({ message: "Something unexpected happend" }); 22 | } 23 | }; 24 | 25 | module.exports = GetHiredCandidate; 26 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetRejectedCandidate.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | const app = express(); 4 | 5 | const GetRejectedCandidates = async (req, res, next) => { 6 | const { id } = req.body; 7 | 8 | try { 9 | const getUser = await Candidate.find( 10 | { 11 | jobID: id, 12 | recruitmentCycle: 'Rejected' 13 | } 14 | ); 15 | if (getUser) { 16 | return res.status(200).json(getUser); 17 | } else { 18 | return res.status(404).json({ message: "No user found" }); 19 | } 20 | } catch (error) { 21 | return res.status(500).json({ message: "Something unexpected happend" }); 22 | } 23 | }; 24 | 25 | module.exports = GetRejectedCandidates; 26 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/UpdateWithdrawnReason.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | 4 | const app = express(); 5 | const UpdateWithdrawnReason = async (req, res, next) => { 6 | 7 | const { id, description } = req.body; 8 | Candidate.findByIdAndUpdate(id, { withdrawn_reason: description }, { new: true }) 9 | .then(updatedItem => { 10 | if (updatedItem) { 11 | return res.status(200).json({ message: ('Updated') }); 12 | } else { 13 | return res.status(404).json({ message: ('Invalid ID') }); 14 | } 15 | }) 16 | .catch(error => { 17 | return res.status(404).json({ message: ('Something went wrong') }); 18 | }); 19 | 20 | } 21 | 22 | module.exports = UpdateWithdrawnReason; -------------------------------------------------------------------------------- /Server/Middleware/AuthMiddleware.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | const secretKey = 'mysecretkey'; // replace with your secret key 3 | 4 | function AuthMiddleware(req, res, next) { 5 | var authHeader = req.headers.authorization; 6 | if (authHeader && authHeader.startsWith('Bearer ')) { 7 | // check if the header starts with "Bearer " prefix 8 | const token = authHeader.split(' ')[1]; // split the header and get the token part 9 | authHeader = token; 10 | } 11 | jwt.verify(authHeader, process.env.KEY, (error, data) => { 12 | if (error) { 13 | return res.status(400).send({ message: "invalid token" }) 14 | } 15 | 16 | else { 17 | req.body.userID = data.id; 18 | next(); 19 | } 20 | }) 21 | } 22 | 23 | module.exports = AuthMiddleware; 24 | -------------------------------------------------------------------------------- /Client/src/Pages/HiredCandidates/HiredCandidatesDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 3 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 4 | import MainAreaOfHiredCandidateDetails from "../../Components/HiredCandidatePage/MainAreaOfHiredCandidateDetails"; 5 | 6 | function HiredCandidateDetails() { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 | 17 |
18 |
19 | ); 20 | } 21 | 22 | export default HiredCandidateDetails; 23 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetWithdrawnCandidate.js: -------------------------------------------------------------------------------- 1 | 2 | const express = require("express"); 3 | const Candidate = require("../../Models/Candidate"); 4 | const app = express(); 5 | 6 | const GetWithdrawnCandidate = async (req, res, next) => { 7 | const { id } = req.body; 8 | 9 | try { 10 | const getUser = await Candidate.find( 11 | { 12 | jobID: id, 13 | recruitmentCycle: 'Withdrawn' 14 | } 15 | ); 16 | 17 | if (getUser) { 18 | return res.status(200).json(getUser); 19 | } else { 20 | return res.status(404).json({ message: "No user found" }); 21 | } 22 | } catch (error) { 23 | return res.status(500).json({ message: "Something unexpected happend" }); 24 | } 25 | }; 26 | 27 | module.exports = GetWithdrawnCandidate; 28 | -------------------------------------------------------------------------------- /Client/src/assets/icons/asd.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 10 | 12 | 13 | -------------------------------------------------------------------------------- /Server/Controllers/Settings/UpdateProfilePicture.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const app = express(); 3 | const Cloudinary = require("cloudinary"); 4 | const cloud = require("../../Config/Cloudnary.js"); 5 | const OrganizationModal = require("../../Models/Organization_Model"); 6 | 7 | const UpdateProfilePicture = async (req, res, next) => { 8 | // // -> Storing selected image to cloud 9 | 10 | const file = req.file; 11 | const userId = req.body.userId; 12 | if (file && userId) { 13 | const findOrganization = await OrganizationModal.findById(userId); 14 | const img = await Cloudinary.v2.uploader.upload(req.file.path); 15 | const { url } = img; 16 | findOrganization.logo = url; 17 | await findOrganization.save(); 18 | res.send("done") 19 | } 20 | }; 21 | 22 | module.exports = UpdateProfilePicture; 23 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/SaveInterviewDateAndTime.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | 4 | const SaveInterviewDateAndTime = async (req, res, next) => { 5 | 6 | const { value, time, id } = req.body; 7 | const date = new Date(value); 8 | const month = date.getMonth() + 1; 9 | const year = date.getFullYear(); 10 | const day = date.getDate(); 11 | 12 | 13 | 14 | const addData = await Candidate.findByIdAndUpdate(id, { 15 | interviewDate: `${day} - ${month} - ${year}`, 16 | interviewTime: `${time}` 17 | }) 18 | 19 | if (addData === null) { 20 | return res.status(500).json({ message: "Something went wrong" }); 21 | } else { 22 | return res.status(200).json({ message: "Added" }); 23 | } 24 | 25 | 26 | 27 | } 28 | 29 | module.exports = SaveInterviewDateAndTime; -------------------------------------------------------------------------------- /Server/Routes/SettingRouter.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const UpdateProfilePicture = require("../Controllers/Settings/UpdateProfilePicture"); 3 | const multer = require('multer'); 4 | const UpdateProfileSettings = require("../Controllers/Settings/UpdateProfileSettings"); 5 | 6 | 7 | 8 | 9 | const SettingRouter = express.Router(); 10 | 11 | // Set up Multer middleware 12 | const storage = multer.diskStorage({ 13 | destination: function (req, file, cb) { 14 | cb(null, 'uploads/'); 15 | }, 16 | filename: function (req, file, cb) { 17 | cb(null, file.originalname); 18 | }, 19 | }); 20 | const upload = multer({ storage }); 21 | 22 | 23 | 24 | SettingRouter.post("/updateProfile", upload.single('file'), UpdateProfilePicture) 25 | SettingRouter.post("/updateProfileData", upload.single('file'), UpdateProfileSettings) 26 | 27 | module.exports = SettingRouter; -------------------------------------------------------------------------------- /Client/src/Components/ProfileSetup/ProfileSetup.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | import Home from "../../assets/illustrations/home.svg"; 4 | function ProfileSetup() { 5 | const navigate = useNavigate(); 6 | return ( 7 |
8 |

9 | Start setting up your company account 10 |

11 | 12 | 13 |
14 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default ProfileSetup; 27 | -------------------------------------------------------------------------------- /Server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "node Server", 9 | "start": "nodemon index.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "axios": "^1.3.0", 16 | "bcrypt": "^5.1.0", 17 | "body-parser": "^1.20.2", 18 | "cloudinary": "^1.34.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.0.3", 21 | "ejs": "^3.1.8", 22 | "express": "^4.18.2", 23 | "express-fileupload": "^1.4.0", 24 | "jsonwebtoken": "^9.0.0", 25 | "mongoose": "^6.9.0", 26 | "morgan": "^1.10.0", 27 | "multer": "^1.4.3", 28 | "ngrok": "^4.3.3", 29 | "nodemailer": "^6.9.1" 30 | }, 31 | "devDependencies": { 32 | "nodemon": "^2.0.20" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/HandleComments.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const CandidateRemarks = require('../../Models/Comment.js'); 3 | const HandleComments = async (req, res, next) => { 4 | const { Applied, Interviewing, Reccomended, Hired, Rejected } = req.body.comment; 5 | const { id } = req.body; 6 | 7 | if (!id) { 8 | return res.status(400).json({ message: "invalid input data" }) 9 | } 10 | const madeComment = await new CandidateRemarks({ 11 | CandidateID: id, 12 | Applied: Applied, 13 | Interviewing: Interviewing, Reccomended: Reccomended, Hired, Rejected, Initialized: true 14 | }); 15 | try { 16 | await madeComment.save(); 17 | } catch (error) { 18 | return res.status(500).json({ message: "Server error occured" }); 19 | } 20 | return res.status(200).json(madeComment) 21 | 22 | 23 | } 24 | 25 | module.exports = HandleComments; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/GetReccomendedCandidates.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | const app = express(); 4 | 5 | const GetReccomendedCandidates = async (req, res, next) => { 6 | //find ID where the id is == candidate 7 | 8 | // -> go check all candidate which have the same job id: 9 | const { id } = req.body; 10 | 11 | try { 12 | const getUser = await Candidate.find({ 13 | jobID: id, 14 | recruitmentCycle: "Reccomended", 15 | }); 16 | 17 | if (getUser) { 18 | return res.status(200).json(getUser); 19 | } else { 20 | return res.status(404).json({ message: "No user found" }); 21 | } 22 | } catch (error) { 23 | return res.status(500).json({ message: "Something unexpected happend" }); 24 | } 25 | }; 26 | 27 | module.exports = GetReccomendedCandidates; 28 | -------------------------------------------------------------------------------- /Server/Controllers/UserController/VerifyMail.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const userModel = require("../../Models/User_Model"); 3 | const app = express(); 4 | 5 | const VerifyMail = async (req, res, next) => { 6 | const id = req.query.id; 7 | 8 | if (!id) { 9 | return res.status(400).json({ error: "ID is required!" }); 10 | } 11 | const findUser = await userModel.findByIdAndUpdate(id, { isVerified: true }); 12 | try { 13 | await findUser.save(); 14 | const html = ` 15 | Verified 16 |

Email verification completed, you can login now.
Go to Home

17 | `; 18 | res.send(html); 19 | } catch (e) { 20 | res.send("Eroor :_ " + e); 21 | } 22 | }; 23 | 24 | module.exports = VerifyMail; 25 | -------------------------------------------------------------------------------- /Client/src/assets/icons/jobs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Client/src/Features/Dashboard/Organization_Details_Slice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from '@reduxjs/toolkit' 2 | 3 | const initialState = { 4 | apiData: null, 5 | loading: false, 6 | error: null, 7 | } 8 | 9 | export const OrganizationDetails = createSlice({ 10 | name: 'organization_details', 11 | initialState, 12 | reducers: { 13 | fetchOrganizationDataStart(state) { 14 | state.loading = true; 15 | state.error = null; 16 | }, 17 | fetchOrganizationDataSuccess(state, action) { 18 | state.loading = false; 19 | state.apiData = action.payload; 20 | }, 21 | fetchOrganizationDataFailure(state, action) { 22 | state.loading = false; 23 | state.error = action.payload; 24 | }, 25 | }, 26 | }) 27 | 28 | export const { fetchOrganizationDataStart, fetchOrganizationDataSuccess, fetchOrganizationDataFailure } = OrganizationDetails.actions 29 | 30 | export default OrganizationDetails.reducer -------------------------------------------------------------------------------- /Server/Controllers/UserController/UpdatePassword.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const userModel = require("../../Models/User_Model"); 3 | const bcrypt = require("bcrypt"); 4 | 5 | const app = express(); 6 | 7 | const updatePassword = async (req, res, next) => { 8 | 9 | const { password, id } = req.body; 10 | console.log(password, id); 11 | if (!password) { 12 | return res.status(400).json({ error: "Must fill new-password field." }); 13 | } 14 | 15 | const saltRounds = 10; 16 | const hashedPassword = await bcrypt.hash(password, saltRounds); 17 | 18 | try { 19 | const findUser = await userModel.findOneAndUpdate( 20 | { _id: id }, 21 | { password: hashedPassword } 22 | ); 23 | await findUser.save(); 24 | return res.status(200).json({ error: "Password updated" }); 25 | } catch (error) { 26 | return res.status(500).json({ error: "Server error, something went wrong" }); 27 | } 28 | }; 29 | 30 | module.exports = updatePassword; 31 | -------------------------------------------------------------------------------- /Client/src/assets/icons/delete.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Client/src/assets/icons/employees.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Client/src/Features/JobCycle/storeAllCandidatesDetails.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | const initialState = { 3 | canidatesData: null, 4 | loading: false, 5 | error: null, 6 | filterBS: null 7 | } 8 | 9 | export const storeAllCandidatesDetails = createSlice({ 10 | name: 'StoreCandidatesDetails', 11 | initialState, 12 | reducers: { 13 | startFetchingCandidatesData(state) { 14 | state.loading = true; 15 | state.error = null; 16 | }, 17 | 18 | sucessOnFetchingCandidatesData(state, action) { 19 | state.loading = false; 20 | state.canidatesData = action.payload 21 | }, 22 | 23 | errorFetchingCandidatesData(state, action) { 24 | state.loading = false, 25 | state.error = action.payload; 26 | 27 | }, 28 | 29 | filterOnBS(state, action) { 30 | return state.filterBS = "Hamza" 31 | } 32 | } 33 | }) 34 | 35 | export const { filterOnBS, startFetchingCandidatesData, sucessOnFetchingCandidatesData, errorFetchingCandidatesData } = storeAllCandidatesDetails.actions; 36 | export default storeAllCandidatesDetails.reducer; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/PatchComments.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const CandidateRemarks = require("../../Models/Comment.js"); 3 | const PatchComments = async (req, res, next) => { 4 | const { Applied, Interviewing, Reccomended, Hired, Rejected } = req.body.comment; 5 | const commentID = req.body.id; 6 | // Update the document by ID 7 | CandidateRemarks.findOneAndUpdate( 8 | { CandidateID: commentID }, // Corrected line 9 | { 10 | Applied: Applied, 11 | Interviewing: Interviewing, 12 | Reccomended: Reccomended, 13 | Hired: Hired, 14 | Rejected: Rejected, 15 | }, 16 | { new: true }) 17 | .then((updatedDoc) => { 18 | if (updatedDoc) { 19 | return res.status(200).json({ message: "Update successful" }); 20 | } else { 21 | 22 | return res.status(400).json({ message: "Candidate not found or an error occurred" }); 23 | } 24 | }) 25 | .catch((error) => { 26 | return res.status(500).json({ message: "Server error" }); 27 | }); 28 | }; 29 | 30 | module.exports = PatchComments; 31 | -------------------------------------------------------------------------------- /Server/Models/User_Model.js: -------------------------------------------------------------------------------- 1 | const mongo = require('mongoose'); 2 | 3 | const userSchema = new mongo.Schema({ 4 | f_name: { 5 | type: String, 6 | required: [true, "First name is must"] 7 | }, 8 | 9 | username: { 10 | type: String, 11 | required: [true, "Last name is must"], 12 | unique: true 13 | }, 14 | 15 | email: { 16 | type: String, 17 | required: [true, 'Email is required'], 18 | unique: true 19 | }, 20 | 21 | company_name: { 22 | type: String, 23 | required: [true, 'Company name is must'] 24 | }, 25 | 26 | password: { 27 | type: String, 28 | required: [true, 'password is required'] 29 | }, 30 | isVerified: { 31 | type: Boolean, default: false 32 | }, 33 | passwordResetToken: { 34 | type: String, 35 | }, 36 | org_registered: { 37 | type: Boolean, default: false 38 | }, 39 | org_id: { 40 | type: String, default: '0' 41 | } 42 | // passwordResetExpires: { 43 | // type: Date, 44 | // } 45 | 46 | }) 47 | 48 | 49 | const userModel = mongo.model('user', userSchema) 50 | 51 | module.exports = userModel; -------------------------------------------------------------------------------- /Server/Controllers/Jobs/PostJob.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Job = require('../../Models/JobModel'); 3 | const app = express(); 4 | const PostJobRouter = async (req, res, next) => { 5 | 6 | const { form, description, org_details } = req.body; 7 | 8 | // console.log(form, description, org_details); 9 | 10 | const Postjob = await new Job({ 11 | jobPosition: form.postition, 12 | officeLocation: form.office_location, 13 | department: form.department, 14 | jobType: form.job_type, 15 | numberOfSeats: form.no_of_seats, 16 | salaryRangeFrom: form.salary_range_from, 17 | salaryRangeUpto: form.salary_range_upto, 18 | job_description: description, 19 | city: org_details[0], 20 | country: org_details[1], 21 | org_name: org_details[2], 22 | org_id: org_details[3] 23 | }) 24 | try { 25 | await Postjob.save(); 26 | 27 | 28 | } catch (error) { 29 | console.log(error) 30 | return res 31 | .status(500) 32 | .json({ error: "An error occurred while saving the user." }); 33 | 34 | } 35 | return res.status(200).json({ message: "Job posted!" }) 36 | 37 | 38 | } 39 | 40 | 41 | module.exports = PostJobRouter; -------------------------------------------------------------------------------- /Client/src/assets/icons/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Client/src/Components/ProfileSetup/SucessModel.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import SucessImg from "../../assets/illustrations/profile_success.svg"; 3 | import { AiFillHome } from "react-icons/ai"; 4 | import { useNavigate } from "react-router-dom"; 5 | 6 | function SucessModel() { 7 | const navigate = useNavigate(); 8 | return ( 9 |
10 | {" "} 11 |
12 | {/* THIS MENUE MAIN INPUT CONTENT */} 13 |
14 |

15 | Congratulations Profile Setup Complete 16 |

17 | 18 | 24 |
25 | 26 | {/* NEXT BUTTON CODE */} 27 | 34 |
35 |
36 | ); 37 | } 38 | 39 | export default SucessModel; 40 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/RejectedCandidates.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FiSend } from "react-icons/fi"; 3 | import { useParams } from "react-router-dom"; 4 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 5 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 6 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 7 | import RejectedCandidateCard from "../../Components/RecruitmentStage/RejectedCandidatesCard"; 8 | 9 | function RejectedCandidates() { 10 | const { id } = useParams(); 11 | 12 | return ( 13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 |

Declined Candidates List

25 | 26 |
27 |
28 |
29 |
30 | ); 31 | } 32 | 33 | export default RejectedCandidates; 34 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/InterviewingCandidate.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 4 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 5 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 6 | import InterviewingCandidateListCard from "../../Components/RecruitmentStage/InterviewingCandidateListCard"; 7 | 8 | function InterviewingCandidate() { 9 | const params = useParams(); 10 | 11 | return ( 12 | <> 13 |
14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 |
22 |
23 |

Interviewing Candidates List

24 |
25 | 26 |
27 |
28 |
29 |
30 | 31 | ); 32 | } 33 | 34 | export default InterviewingCandidate; 35 | -------------------------------------------------------------------------------- /Client/src/Pages/Employees/MainPageOfEmployees.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 4 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 5 | import Illustration from "../../assets/illustrations/no_user.svg"; 6 | function MainPageOfEmployees() { 7 | return ( 8 |
9 |
10 | 11 |
12 |
13 |
14 | 15 |
16 |
17 |

Your Employees

18 | 19 | 22 | 23 |
24 | 25 | 31 |

32 | Currently no active employee 33 |

34 |
35 |
36 | ); 37 | } 38 | 39 | export default MainPageOfEmployees; 40 | -------------------------------------------------------------------------------- /Server/Controllers/UserController/verifyForgetpwd.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const userModel = require("../../Models/User_Model"); 3 | const app = express(); 4 | 5 | const verifyForgetPwd = async (req, res, next) => { 6 | 7 | const { params, inputCode } = req.body; 8 | 9 | console.log("This is the otp code") 10 | console.log(params, inputCode); 11 | //to remove space and , from code (as input is coming from an array[]) 12 | let newVal = inputCode.toString(); 13 | newVal = newVal.split(" ").pop() 14 | newVal = newVal.replace(/,/g, ''); 15 | 16 | 17 | 18 | try { 19 | console.log(newVal, params) 20 | const user = await userModel.findOneAndUpdate( 21 | { 22 | $and: [{ email: params }, { passwordResetToken: newVal }], 23 | }, 24 | { passwordResetToken: null } 25 | ); 26 | if (!user) { 27 | console.log('invalid otp'); 28 | return res.status(404).json({ error: "Invalid otp_code" }); 29 | } 30 | 31 | res.status(200).json({ id: user._id }) 32 | } catch (error) { 33 | return res.status(500).json({ error: "Something went wrong" }); 34 | } 35 | //this params will shared to the next route with req.data method. 36 | req.data = { 37 | params: params, 38 | }; 39 | next(); 40 | }; 41 | 42 | module.exports = verifyForgetPwd; 43 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/DeleteCandidateProfile.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Candidate = require('../../Models/Candidate'); 3 | 4 | const DeleteCandidateProfile = async (req, res, next) => { 5 | 6 | const { id } = req.body; 7 | 8 | console.log(id); 9 | try { 10 | const candidate = await Candidate.findByIdAndDelete({ _id: id }); 11 | console.log(candidate); 12 | if (!candidate) { 13 | return res.status(404).json({ message: 'Candidate not found' }); 14 | } 15 | 16 | await Candidate.findByIdAndDelete(req.params.id); 17 | 18 | return res.status(200).json({ message: 'Candidate deleted successfully' }); 19 | } catch (err) { 20 | console.error(err.message); 21 | return res.status(500).json({ message: 'Server error' }); 22 | } 23 | }; 24 | 25 | // const { id } = req.body; 26 | // console.log(id); 27 | // const findUser = await Candidate.findByIdAndRemove(id); 28 | 29 | // if (findUser) { 30 | // try { 31 | // findUser.save(); 32 | 33 | // } catch (error) { 34 | // return res.status(500).json({ message: "Server Error" }) 35 | // } 36 | 37 | // return res.status(200).json({ message: "User delete successfully" }) 38 | // } 39 | // else { 40 | // return res.status(404).json({ message: "No user found" }) 41 | // } 42 | 43 | 44 | 45 | 46 | module.exports = DeleteCandidateProfile; -------------------------------------------------------------------------------- /Client/src/Components/Common/GoBackButton.jsx: -------------------------------------------------------------------------------- 1 | import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from "@chakra-ui/react"; 2 | import React from "react"; 3 | import { FiArrowLeft } from "react-icons/fi"; 4 | import { useNavigate } from "react-router-dom"; 5 | 6 | function GoBackButton({ name, location }) { 7 | const navigate = useNavigate(); 8 | return ( 9 |
10 |
11 |
{ 16 | navigate(-1); 17 | }} 18 | > 19 | 20 |
21 | 22 | 23 | 24 | { 26 | navigate(-1); 27 | }} 28 | > 29 | {location} 30 | 31 | 32 | 33 | 34 | 35 | {name} 36 | {/* {candidateDetails?.firstName} */} 37 | 38 | 39 | 40 |
41 |
42 | ); 43 | } 44 | 45 | export default GoBackButton; 46 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/WithdrawnCandidate.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FiSend } from "react-icons/fi"; 3 | import { useParams } from "react-router-dom"; 4 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 5 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 6 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 7 | import RejectedCandidateCard from "../../Components/RecruitmentStage/RejectedCandidatesCard"; 8 | import WithdrawnCandidateCard from "../../Components/RecruitmentStage/WithdrawnCandidateCard"; 9 | 10 | function WithdrawnCandidate() { 11 | const { id } = useParams(); 12 | 13 | return ( 14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |

Withdrawn Candidates List

26 | 27 |
28 |
29 |
30 |
31 | ); 32 | } 33 | 34 | export default WithdrawnCandidate; 35 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/HiredCandidates.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { FiSend } from "react-icons/fi"; 3 | import { useParams } from "react-router-dom"; 4 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 5 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 6 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 7 | import HiredCandidateCard from "../../Components/RecruitmentStage/HiredCandidateCard"; 8 | import ReccomendidCandidateCard from "../../Components/RecruitmentStage/ReccomendidCandidateCard"; 9 | 10 | function HiredCandidates() { 11 | const { id } = useParams(); 12 | 13 | return ( 14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 | 22 | 23 |
24 |
25 |

Hired Candidates List

26 |
27 | 28 |
29 |
30 |
31 |
32 |
33 | ); 34 | } 35 | 36 | export default HiredCandidates; 37 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/ReccomendedCandidates.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 4 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 5 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 6 | import InterviewingCandidateListCard from "../../Components/RecruitmentStage/InterviewingCandidateListCard"; 7 | import ReccomendidCandidateCard from "../../Components/RecruitmentStage/ReccomendidCandidateCard"; 8 | 9 | function ReccomendedCandidates() { 10 | const { id } = useParams(); 11 | 12 | return ( 13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | 22 |
23 |
24 |

Reccomended Candidates List

25 |
26 | 27 |
28 |
29 |
30 |
31 |
32 | ); 33 | } 34 | 35 | export default ReccomendedCandidates; 36 | -------------------------------------------------------------------------------- /Server/Routes/ProfileCreation.js: -------------------------------------------------------------------------------- 1 | // -> IMPORTS 2 | const ProfileRouter = require('../Controllers/Setup Profile/Profile_Setup'); 3 | const multer = require('multer') 4 | // -> INITIALIZATIONS 5 | const express = require('express'); 6 | const AuthMiddleware = require('../Middleware/AuthMiddleware'); 7 | const VerifyToken = require('../Middleware/VerifyToken'); 8 | const ProfileSetup = express.Router(); 9 | 10 | //* ** MULTER CONFIGURATION ** */ 11 | 12 | const storage = multer.diskStorage({ 13 | destination: function (req, file, cb) { 14 | //so it will store the images in the public directory 15 | cb(null, "Models"); 16 | }, 17 | filename: function (req, file, cb) { 18 | cb(null, new Date().toISOString() + file.originalname); 19 | } 20 | }); 21 | 22 | const multerFilter = (req, file, cb) => { 23 | if ( 24 | file.mimetype === "image/png" || 25 | file.mimetype === "image/jpg" || 26 | file.mimetype === "image/jpeg" 27 | ) { 28 | cb(null, true); 29 | } else { 30 | cb(null, false); 31 | } 32 | }; 33 | 34 | const upload = multer({ storage: storage, multerFilter }); 35 | 36 | // ******************************************************************* 37 | 38 | 39 | // -> MAIN CODE 40 | 41 | //1st Time Profile Setup Route 42 | ProfileSetup.post("/setup", AuthMiddleware, ProfileRouter); 43 | 44 | ProfileSetup.post("/", (req, res) => { 45 | res.send("welcome") 46 | }) 47 | // -> EXPORT 48 | module.exports = ProfileSetup; 49 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/AppliedCandidateDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import { ToastContainer, toast } from "react-toastify"; 4 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 5 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 6 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 7 | import SwitchStatus from "../../Components/RecruitmentStage/SwitchStatus"; 8 | import AppliedApplicantProfile from "./AppliedApplicantProfile"; 9 | 10 | function AppliedCandidateDetails() { 11 | const { id } = useParams(); 12 | 13 | const notify = () => toast("Wow so easy !"); 14 | 15 | return ( 16 | <> 17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 | 25 |
26 | 27 |
28 |
29 |
30 | 31 |
32 |
33 |
34 | 35 | ); 36 | } 37 | 38 | export default AppliedCandidateDetails; 39 | -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/WithdrawnDetails.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 4 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 5 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 6 | import SwitchStatus from "../../Components/RecruitmentStage/SwitchStatus"; 7 | import WithdrawnCandidateCard from "../../Components/RecruitmentStage/WithdrawnCandidateCard"; 8 | import WithdrawnDetailsCard from "../../Components/RecruitmentStage/WithdrawnDetailsCard"; 9 | 10 | function WithdrawnDetails() { 11 | const { id } = useParams(); 12 | return ( 13 |
14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 | 22 |
23 | 24 |
25 | 26 | {/* */} 27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 |
35 | ); 36 | } 37 | 38 | export default WithdrawnDetails; 39 | -------------------------------------------------------------------------------- /Client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "smart-cruiter", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@chakra-ui/react": "^2.6.1", 13 | "@reduxjs/toolkit": "^1.9.3", 14 | "@smastrom/react-rating": "^1.3.1", 15 | "@vercel/analytics": "^1.0.1", 16 | "axios": "^1.3.1", 17 | "chart.js": "^4.3.0", 18 | "daisyui": "^2.49.0", 19 | "formik": "^2.2.9", 20 | "framer-motion": "^10.12.10", 21 | "js-alert": "^2.0.0", 22 | "react": "^18.2.0", 23 | "react-animated-rating": "^1.0.5", 24 | "react-calendar": "^4.2.1", 25 | "react-confetti": "^6.1.0", 26 | "react-country-region-selector": "^3.6.1", 27 | "react-datepicker": "^4.11.0", 28 | "react-dom": "^18.2.0", 29 | "react-icons": "^4.7.1", 30 | "react-quill": "^2.0.0", 31 | "react-redux": "^8.0.5", 32 | "react-router-dom": "^6.8.0", 33 | "react-simple-star-rating": "^5.1.7", 34 | "react-spinners": "^0.13.8", 35 | "react-time-picker": "^6.1.0", 36 | "react-toastify": "^9.1.2", 37 | "react-use-file-upload": "^0.9.5", 38 | "recharts": "^2.6.2", 39 | "yup": "^0.32.11" 40 | }, 41 | "devDependencies": { 42 | "@types/react": "^18.0.26", 43 | "@types/react-dom": "^18.0.9", 44 | "@vitejs/plugin-react": "^3.0.0", 45 | "autoprefixer": "^10.4.13", 46 | "postcss": "^8.4.21", 47 | "tailwindcss": "^3.2.4", 48 | "vite": "^4.0.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Server/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | require("dotenv").config(); 3 | const ngrok = require('ngrok'); 4 | 5 | // const fileUpload = require('express-fileupload') 6 | var bodyParser = require('body-parser') 7 | const mongoose = require('mongoose'); 8 | const cors = require("cors"); 9 | const connection = require("./Config/Database.js"); 10 | const UserRouter = require("./Routes/UserRoute"); 11 | const ProfileRouter = require("./Routes/ProfileCreation"); 12 | const JobRouter = require('./Routes/Jobs'); 13 | const RecruitmentRouter = require('./Routes/RecruitmentCycle.js'); 14 | const ReportRouter = require('./Routes/Report.js'); 15 | const RouterReport = require('./Routes/Report.js'); 16 | const SettingRouter = require('./Routes/SettingRouter.js'); 17 | 18 | // -----| Configration |----- 19 | const app = express(); 20 | app.use(cors()); 21 | mongoose.set('strictQuery', false); 22 | app.use(express.urlencoded({ extended: true })) 23 | // app.use(fileUpload()) 24 | app.use(express.json()); 25 | app.use(bodyParser.json()); 26 | 27 | connection(); 28 | 29 | 30 | 31 | //Routes 32 | 33 | 34 | app.use('/', UserRouter); 35 | app.use("/profile", ProfileRouter) 36 | app.use("/job", JobRouter) 37 | app.use('/details', RecruitmentRouter) 38 | app.use("/report", RouterReport) 39 | app.use("/settings", SettingRouter); 40 | 41 | // Create a new ngrok tunnel. 42 | // 43 | 44 | 45 | 46 | //-----| App listening |------ 47 | const port = process.env.PORT || 8080; 48 | 49 | 50 | 51 | app.listen(port, () => { 52 | console.log("server is running on port :" + port) 53 | }) 54 | 55 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/UpdateStatus.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | const Job = require("../../Models/JobModel"); 4 | const app = express(); 5 | const UpdateStatus = async (req, res, next) => { 6 | const { status, id } = req.body; 7 | if (!status) { 8 | return res.status(300).json({ message: "Status value is empty" }); 9 | } 10 | try { 11 | const updatedCandidate = await Candidate.findByIdAndUpdate( 12 | id, 13 | { recruitmentCycle: status }, 14 | { new: true } 15 | ); 16 | 17 | if (updatedCandidate) { 18 | if (status == "Declined") { 19 | const findJob = await Job.findById(updatedCandidate.jobID); 20 | findJob.report_status.rejected += 1; 21 | await findJob.save(); 22 | } 23 | else if (status == "Hired") { 24 | const findJob = await Job.findById(updatedCandidate.jobID); 25 | findJob.report_status.hired += 1; 26 | await findJob.save(); 27 | } 28 | 29 | return res.status(200).json({ message: "Update successful" }); 30 | } else { 31 | return res 32 | .status(400) 33 | .json({ message: "Candidate not found or an error occurred" }); 34 | } 35 | } catch (error) { 36 | console.error("Error updating candidate:", error); 37 | return res.status(500).json({ message: "Server error occurred" }); 38 | } 39 | }; 40 | 41 | module.exports = UpdateStatus; 42 | -------------------------------------------------------------------------------- /Client/src/assets/icons/notification.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Client/src/assets/icons/candidates.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/SubmitFeedbacl.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | const app = express(); 4 | 5 | const SubmitFeedback = async (req, res, next) => { 6 | 7 | 8 | const { id, startRating } = req.body; 9 | 10 | //now i want to enfore that if user by mistake put wrong value he should be able to put new value again 11 | 12 | var user = await Candidate.findById(id); 13 | if (user) { 14 | if (user.feedback_form == null || user.feedback_form == "") { 15 | user.feedback_form = [ 16 | startRating.first, 17 | startRating.second, 18 | startRating.third, 19 | startRating.fourth, 20 | startRating.fifth, 21 | ]; 22 | 23 | await user.save(); 24 | return res.status(200).json({ message: "Saved" }); 25 | } else { 26 | //so else part is dealing that if there is alrady any value 27 | // in object of -> feedback_form 28 | // than 1st i will clean that value and than add the new value 29 | 30 | let abc = user.feedback_form; 31 | 32 | abc = null; 33 | user.feedback_form = abc; 34 | 35 | user.feedback_form = [ 36 | startRating.first, 37 | startRating.second, 38 | startRating.third, 39 | startRating.fourth, 40 | startRating.fifth, 41 | ]; 42 | await user.save(); 43 | return res.status(200).json({ message: "Saved" }); 44 | } 45 | } else { 46 | return res.status(500).json({ message: "Something went wrong" }); 47 | } 48 | }; 49 | 50 | module.exports = SubmitFeedback; 51 | -------------------------------------------------------------------------------- /Server/Routes/UserRoute.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const GetProfilePicture = require('../Controllers/Dashboard/GetProfilePic.js'); 3 | const Home = require('../Controllers/Dashboard/Home.js'); 4 | const forget_password = require('../Controllers/UserController/ForgetPassword.js'); 5 | const login = require('../Controllers/UserController/Login.js'); 6 | const register = require('../Controllers/UserController/Register.js'); 7 | const updatePassword = require('../Controllers/UserController/UpdatePassword.js'); 8 | const verifyForgetPwd = require('../Controllers/UserController/verifyForgetpwd.js'); 9 | const VerifyMail = require('../Controllers/UserController/VerifyMail.js'); 10 | const AuthMiddleware = require('../Middleware/AuthMiddleware.js'); 11 | const VerifyToken = require('../Middleware/VerifyToken.js'); 12 | 13 | const UserRouter = express.Router(); 14 | 15 | 16 | 17 | //Login Route 18 | UserRouter.post("/login", login); 19 | 20 | 21 | 22 | 23 | // -> Register 24 | 25 | UserRouter.post("/register", register) 26 | 27 | // -> Verify Email 28 | 29 | UserRouter.get("/verify", VerifyMail) 30 | 31 | 32 | // -> Forget Password 33 | UserRouter.post("/forget-password", forget_password) 34 | 35 | // -> Verify Forget Password by verifying token 36 | UserRouter.post("/verify-forget-pwd", verifyForgetPwd) 37 | 38 | //-> Making a new password 39 | UserRouter.post("/new-password", updatePassword) 40 | 41 | 42 | 43 | 44 | 45 | //-> ## DASHBOARD HOME ROUTES ## 46 | 47 | //I left Auth Middleware intentionally to increase devlopment speed 48 | // Will implement it while deploying it on any hosting platform 49 | UserRouter.post("/home", AuthMiddleware, VerifyToken) 50 | UserRouter.post("/dashboard", Home) 51 | UserRouter.post("/getProfilePic", GetProfilePicture) 52 | module.exports = UserRouter; -------------------------------------------------------------------------------- /Server/Routes/Jobs.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const ApplyForJob = require('../Controllers/Jobs/ApplyForJob'); 3 | const GetAllPostedJobs = require('../Controllers/Jobs/GetAllPostedJobs'); 4 | const GetJob = require('../Controllers/Jobs/GetJobs'); 5 | const GetSelectedJobDescription = require('../Controllers/Jobs/GetSelectedJobDescription'); 6 | const PostJobRouter = require('../Controllers/Jobs/PostJob'); 7 | const multer = require('multer'); 8 | const GetOrganizationPostedJobApplicants = require('../Controllers/Jobs/GetOrganizationPostedJob'); 9 | const FilterShowActiveJobs = require('../Controllers/Jobs/Filter-ShowActiveJobs'); 10 | const FilterShowClosedJobs = require('../Controllers/Jobs/FilterShowClosedJobs'); 11 | 12 | 13 | const JobRouter = express.Router(); 14 | 15 | JobRouter.post("/post", PostJobRouter) 16 | JobRouter.post("/get-jobs", GetJob); 17 | JobRouter.post("/get-jobs/active", FilterShowActiveJobs); 18 | JobRouter.post("/get-jobs/closed", FilterShowClosedJobs); 19 | JobRouter.post("/get-jobs/details", GetSelectedJobDescription); 20 | JobRouter.get("/get-all-jobs", GetAllPostedJobs) 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | //* ~~~~~~~~FILE - UPLOAD - API ~~~~~~~~~~ **/ 34 | const storage = multer.diskStorage({ 35 | destination: function (req, file, cb) { 36 | cb(null, 'uploads/'); 37 | }, 38 | filename: function (req, file, cb) { 39 | cb(null, Date.now() + '-' + file.originalname); 40 | } 41 | }); 42 | const upload = multer({ storage: storage }); 43 | // The correct method was to use upload.array() but it was giving error 44 | // so for time being i am gona use .any method 45 | 46 | JobRouter.post("/apply-to-job", upload.any('profile', 'resume'), ApplyForJob) 47 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 48 | 49 | 50 | 51 | 52 | 53 | 54 | module.exports = JobRouter; -------------------------------------------------------------------------------- /Client/src/Pages/RecruitmentCycle/ReccomendedCandidatesDetails.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import TopRcruitementCycle from "../../Components/Dashboard/CreateJob/TopRcruitementCycle"; 3 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 4 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 5 | import SwitchStatus from "../../Components/RecruitmentStage/SwitchStatus"; 6 | import GoBackButton from "../../Components/Common/GoBackButton"; 7 | import ReccomendedCandidateDetailsCard from "../../Components/RecruitmentStage/ReccomendedCandidateDetailsCard"; 8 | import { useParams } from "react-router-dom"; 9 | function ReccomendedCandidatesDetails() { 10 | const { id } = useParams(); 11 | const [user, SetUser] = useState(); 12 | 13 | const [candidateID, setCandidateID] = useState(); 14 | 15 | return ( 16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 |
29 |
30 | 31 |
32 | 38 |
39 |
40 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default ReccomendedCandidatesDetails; 47 | -------------------------------------------------------------------------------- /Client/src/Pages/CreateJob/CreateJob.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { useSelector } from "react-redux"; 4 | import { Link, useNavigate } from "react-router-dom"; 5 | import CreatedJobElement from "../../Components/Dashboard/CreateJob/CreatedJobElement"; 6 | import CreateJobHeadaer from "../../Components/Dashboard/CreateJob/CreateJobHeadaer"; 7 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 8 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 9 | function CreateJob() { 10 | const [data, setData] = useState(); 11 | useEffect(() => { 12 | const fetchData = async () => { 13 | // axios POST request 14 | const options = { 15 | url: "https://smart-cruiter-fyp-production.up.railway.app/job/get-jobs", 16 | method: "POST", 17 | headers: { 18 | Accept: "application/json", 19 | "Content-Type": "application/json;charset=UTF-8", 20 | }, 21 | data: { id: localStorage.getItem("organization_id") }, 22 | }; 23 | 24 | axios(options).then((response) => { 25 | // console.log(response); 26 | 27 | setData(response.data.jobs); 28 | }); 29 | }; 30 | 31 | fetchData(); 32 | }, []); 33 | 34 | return ( 35 |
36 |
37 | 38 |
39 |
40 |
41 | 42 | 43 | 44 |
45 | 46 |
47 | 48 |
49 |
50 |
51 | ); 52 | } 53 | 54 | export default CreateJob; 55 | -------------------------------------------------------------------------------- /Client/src/Components/Dashboard/ProfileCreation/NavigationTab.jsx: -------------------------------------------------------------------------------- 1 | import { background } from "@chakra-ui/react"; 2 | import React from "react"; 3 | 4 | function NavigationTab({ 5 | first_value, 6 | second_value, 7 | third_value, 8 | fourth_value, 9 | active, 10 | text, 11 | }) { 12 | return ( 13 |
14 |
15 |
19 | {text == 1 ? 1 : null} 20 |
21 |

{first_value}

22 |
23 | 24 |
25 |

{second_value}

26 | 27 |
31 | {text == 2 ? 2 : null} 32 |
33 |
34 | 35 |
36 |
40 | {text == 3 ? 3 : null} 41 |
42 |

{third_value}

43 |
44 | 45 |
46 |

{fourth_value}

47 | 48 |
52 | {text == 4 ? 4 : null} 53 |
54 |
55 |
56 | ); 57 | } 58 | 59 | export default NavigationTab; 60 | -------------------------------------------------------------------------------- /Client/src/Components/Dashboard/CreateJob/TopRcruitementCycle.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useEffect } from "react"; 3 | import { NavLink, useNavigate } from "react-router-dom"; 4 | 5 | function TopRcruitementCycle({ id }) { 6 | // const [ids, setID] = useState(); 7 | // useEffect(() => { 8 | // setID(id); 9 | // }, [id]); 10 | 11 | const NavLinkStyles = ({ isActive }) => { 12 | return { 13 | background: isActive ? "#0063B2" : "#2687F0", 14 | color: "white", 15 | padding: "0.75rem", 16 | borderRadius: "0.5rem", 17 | fontFamily: "Poppins , sans-serif", 18 | fontWeight: 300, 19 | fontSize: "14px", 20 | lineHeight: "28px", 21 | }; 22 | }; 23 | 24 | // console.log(id); 25 | return ( 26 |
27 |
28 | {/* */} 31 | 32 | 33 | Applied Candidates 34 | 35 | 36 | 37 | Interviewing 38 | 39 | 40 | 41 | Reccomended 42 | 43 | 44 | 45 | Hired 46 | 47 | 48 | 49 | Declined 50 | 51 | 52 | 53 | Withdrawn 54 | 55 |
56 |
57 | ); 58 | } 59 | 60 | export default TopRcruitementCycle; 61 | -------------------------------------------------------------------------------- /Server/Models/Organization_Model.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | 4 | const OrganizationSchema = new mongoose.Schema({ 5 | 6 | username: { 7 | type: String, 8 | required: [true, 'Email is required'], 9 | unique: true 10 | }, 11 | 12 | 13 | organization_name: { 14 | 15 | type: String, 16 | required: [true, 'Name is required'], 17 | 18 | }, 19 | phoneNo: { 20 | type: Number, 21 | required: [true, 'Phone No is required'], 22 | }, 23 | website: { 24 | type: String, 25 | required: [true, 'Website URL is required'], 26 | }, 27 | logo: { 28 | type: String, 29 | required: true 30 | }, 31 | departments: { 32 | type: [String], 33 | min: 1, 34 | 35 | }, 36 | office_address: { 37 | type: String, 38 | required: [true, 'Address is required'], 39 | }, 40 | office_city: { 41 | type: String, 42 | required: [true, 'Office City is required'], 43 | }, 44 | office_country: { 45 | type: String, 46 | required: [true, 'Office Country is required'], 47 | }, 48 | 49 | fb_url: { 50 | type: String, 51 | required: [true, 'FB URL is required'], 52 | }, 53 | linkedIn_url: { 54 | type: String, 55 | required: [true, 'LinkedIn URL is required'], 56 | }, 57 | insta_url: { 58 | type: String, 59 | required: [true, 'Insta URL is required'], 60 | }, 61 | yt_url: { 62 | type: String, 63 | required: [true, 'YT URL is required'], 64 | }, 65 | // team_members: [ 66 | 67 | // { 68 | // name: String, 69 | // email: String, 70 | // role: String 71 | // } 72 | // ] 73 | team_members: [{ 74 | 75 | name: { type: String, required: true }, 76 | email: { type: String, required: true, max: 20 }, 77 | role: { type: String, required: true } 78 | 79 | }] 80 | 81 | }) 82 | 83 | 84 | const OrganizationModal = mongoose.model('organization', OrganizationSchema); 85 | 86 | module.exports = OrganizationModal; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Smart Cruiter 3 | ### Live Link : https://smart-cruiter-fyp-mqjf.vercel.app 4 | An applicant tracking management system, help organization in their hiring process with futures like: 5 | 6 | 1-) Job creation 7 | 8 | 2-) Managing applicants (applied, recommended, hired , declined, withdrawn) 9 | 10 | 3-) You can set up an online interview 11 | 12 | 4-) Send rejection or accepting emails to hundreds of candidates in a single click 13 | 14 | 5-) Manage the record of applicants with great data visualization 15 | 16 | 6-) Job details and application form for end-user 17 | 18 | 19 | 20 | ## Tech Stack 21 | 22 | **Client:** React, Redux Toolkit, TailwindCSS, DaisyUI 23 | 24 | **Server:** Node, Express 25 | 26 | **Database:** MongoDB Atlas 27 | 28 | **Deployment:** Vercel 29 | 30 | 31 | 32 | ## Screenshots 33 | 34 | ![Screenshot from 2023-05-28 18-07-00](https://github.com/Hamza-Sajid/Smart-Cruiter-FYP/assets/20709155/c1928811-95d5-44c5-b3e1-b53e657222e6) 35 | ![Screenshot from 2023-05-28 18-12-36](https://github.com/Hamza-Sajid/Smart-Cruiter-FYP/assets/20709155/b14ffb26-8964-4332-ab39-cd2f5d96287d) 36 | ![Screenshot from 2023-05-28 18-12-44](https://github.com/Hamza-Sajid/Smart-Cruiter-FYP/assets/20709155/86507d07-9b93-4750-a50b-06e034ca3c25) 37 | ![Screenshot from 2023-05-28 18-12-52](https://github.com/Hamza-Sajid/Smart-Cruiter-FYP/assets/20709155/f5e55181-53be-4531-933e-827667ec85a5) 38 | 39 | 40 | 41 | ## Installation 42 | 43 | To install dependencies use npm 44 | 45 | ```bash 46 | npm install 47 | ``` 48 | Backend / Server start command: 49 | ```bash 50 | npm run start 51 | Nodemon index 52 | ``` 53 | 54 | Frontend / Client start command: 55 | ```bash 56 | npm run dev 57 | ``` 58 | 59 | ## Need Detailed Assistance/Help? 60 | I'm available as a freelance MERN developer on [Fiverr](https://www.fiverr.com/mrhamzasajid/build-web-applications-with-mern-stack-reactjs-nodejs?context_referrer=seller_page&ref_ctx_id=e16faa02db8d4fd0bddae79455778a9c&pckg_id=1&pos=3&seller_online=true&imp_id=9d7c1f8d-2382-4e0d-8525-78ada752dfcc#), if you want to convert your idea to code or struggling with bugs in your system feel free to contact me https://www.fiverr.com/mrhamzasajid 61 | 62 | -------------------------------------------------------------------------------- /Server/Controllers/UserController/Login.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | const bcrypt = require("bcrypt"); 4 | const jwt = require("jsonwebtoken"); 5 | const userModel = require("../../Models/User_Model.js"); 6 | 7 | const secret = process.env.KEY; 8 | 9 | const login = async (req, res, next) => { 10 | const { email, password } = req.body; 11 | 12 | if (!email || !password) { 13 | return res.status(400).json({ error: "All fields are required." }); 14 | } 15 | try { 16 | 17 | 18 | const findUser = await userModel.findOne({ email: email }); 19 | if (!findUser) { 20 | return res.status(404).json({ 21 | error: "No such user found", 22 | }); 23 | } 24 | 25 | // ************************************** 26 | // -> INITIAL LOGIC || JUST BLAZING-UP 27 | // ************************************** 28 | // const checkStatus = await userModel.findOne({ isVerified: false }); 29 | // if (checkStatus) { 30 | // console.log(checkStatus); 31 | // return res.status(403).json({ 32 | // error: "Email isn't verified, kindly first verify your email address", 33 | // }); 34 | // } 35 | 36 | const checkStatus = findUser.isVerified; 37 | if (checkStatus == false) { 38 | return res.status(403).json({ 39 | error: "Email isn't verified, kindly first verify your email address", 40 | }); 41 | } 42 | 43 | 44 | 45 | // Compare the passwords 46 | const unhashed = await bcrypt.compare(password, findUser.password); 47 | if (!unhashed) { 48 | return res.status(401).json({ 49 | error: "Incorrect password", 50 | }); 51 | } 52 | //putting user id in pyaload so find user uniquely 53 | const payload = { 54 | id: findUser._id, 55 | }; 56 | const token = jwt.sign(payload, process.env.KEY, { expiresIn: "96h" }); 57 | return res.status(200).json({ 58 | message: "Login successful", 59 | token: token, 60 | }); 61 | } 62 | 63 | catch (e) { 64 | res.send("Something un-expected happend! " + e) 65 | } 66 | }; 67 | module.exports = login; 68 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/SendInterviewEmail.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const app = express(); 3 | const nodemailer = require('nodemailer'); 4 | 5 | 6 | 7 | const SendInterviewEmail = async (req, res, next) => { 8 | 9 | // const { to, subject, body } = req.body; 10 | 11 | const { discription } = req.body; 12 | const { emailDetails } = req.body 13 | const { to } = emailDetails; 14 | const { subject } = emailDetails; 15 | 16 | 17 | if (!to || !subject || !discription) { 18 | return res.status(206).json({ message: "Complete all required fields" }) 19 | } 20 | 21 | 22 | 23 | const sendMail = async () => { 24 | 25 | const htmlCode = ` 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 |

Interview Invitation

38 |
39 |
40 | 41 | 42 |
43 | ${discription 44 | } 45 |
46 | 47 | 48 |
49 |
50 |

51 | Powered By : Smart-Cruiter 52 |

53 | 54 | 55 | 56 | ` 57 | try { 58 | 59 | const transporter = nodemailer.createTransport({ 60 | host: 'smtp.gmail.com', 61 | port: 587, 62 | secure: false, 63 | auth: { 64 | user: process.env.MAILUSER, 65 | pass: process.env.MAILPASS, 66 | }, 67 | }); 68 | 69 | const mailOptions = { 70 | from: process.env.MAILUSER, 71 | to: to, 72 | subject: subject, 73 | html: htmlCode, 74 | }; 75 | 76 | transporter.sendMail(mailOptions, (error, info) => { 77 | if (error) { 78 | 79 | return res.status(500).json({ message: "Something went wrong" }) 80 | } else { 81 | return res.status(200).json({ message: "email sent succesfully" }) 82 | 83 | } 84 | }); 85 | 86 | } catch (error) { 87 | return res.status(500).json({ message: "Something went wrong" }) 88 | } 89 | } 90 | 91 | sendMail(); 92 | 93 | 94 | } 95 | 96 | module.exports = SendInterviewEmail; -------------------------------------------------------------------------------- /Client/src/Pages/Employees/AddNewEmployee.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 3 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 4 | import ProfilePic from "../../assets/icons/profileIcon.png"; 5 | function AddNewEmployee() { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |

18 | Add employee information 19 |

20 | 21 | 22 |
23 | 26 | 31 | 32 | 35 | 40 | 41 | 44 | 52 |
53 | 56 |
57 |
58 |
59 | ); 60 | } 61 | 62 | export default AddNewEmployee; 63 | -------------------------------------------------------------------------------- /Client/src/App.css: -------------------------------------------------------------------------------- 1 | 2 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500&display=swap'); 3 | 4 | @import url('https://fonts.googleapis.com/css2?family=Lato:wght@700&display=swap'); 5 | 6 | @import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap'); 7 | 8 | /* DIV SHADOWS - BOX_SHADOWS */ 9 | .shadows{ 10 | background: #FFFFFF; 11 | /* Shadow/Div-Shadow */ 12 | box-shadow: 0px 1px 3px -1px rgba(0, 0, 0, 0.3), 0px 2px 5px -1px rgba(50, 50, 93, 0.25); 13 | border-radius: 5px;} 14 | 15 | .modalShadow{ 16 | box-shadow: 0px 2px 8px rgba(99, 99, 99, 0.2); 17 | border-radius: 5px; 18 | } 19 | 20 | /* FONTS */ 21 | .heading1 22 | { 23 | font-family: 'Poppins', sans-serif; 24 | font-weight: 500; 25 | font-size: 48px; 26 | line-height: 28px; 27 | color: #333232; 28 | } 29 | .heading2 30 | { 31 | font-family: 'Poppins', sans-serif; 32 | font-weight: 500; 33 | font-size: 32px; 34 | line-height: 28px; 35 | color: #333232; 36 | } 37 | 38 | .heading2b 39 | { 40 | font-family: 'Poppins', sans-serif; 41 | font-weight: 500; 42 | font-size: 26px; 43 | line-height: 28px; 44 | color: #333232; 45 | } 46 | 47 | .heading3 48 | { 49 | font-family: 'Poppins'; 50 | font-style: normal; 51 | font-weight: 300; 52 | font-size: 20px; 53 | line-height: 28px; 54 | color: #333232; 55 | } 56 | .heading4 57 | { 58 | font-family: 'Poppins', sans-serif; 59 | font-weight: 300; 60 | font-size: 16px; 61 | line-height: 28px; 62 | color: #333232; 63 | } 64 | .line1 65 | { 66 | font-family: 'Poppins', sans-serif; 67 | font-weight: 400; 68 | font-size: 16px; 69 | line-height: 28px; 70 | } 71 | 72 | 73 | 74 | .line2 75 | { 76 | font-family: 'Poppins', sans-serif; 77 | font-weight: 300; 78 | font-size: 14px; 79 | line-height: 28px; 80 | } 81 | 82 | .btnfont 83 | { 84 | font-family: 'Poppins', sans-serif; 85 | font-weight: 500; 86 | font-size: 24px; 87 | line-height: 28px; 88 | } 89 | 90 | 91 | .navMenuFont 92 | { 93 | font-family: 'Poppins', sans-serif; 94 | font-style: normal; 95 | font-weight: 300; 96 | font-size: 17px; 97 | line-height: 28px; 98 | color: #333232; 99 | } 100 | 101 | .topNavigationBoxShadow{ 102 | box-shadow: 0px 1px 4px rgba(74, 188, 227, 0.25); 103 | 104 | } 105 | 106 | 107 | /* COLORS */ 108 | 109 | .secondryColor 110 | { 111 | background: #2687F0; 112 | } 113 | 114 | .tertaryColor 115 | { 116 | background: #E3F3FF; 117 | } -------------------------------------------------------------------------------- /Client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 19 | 20 | 21 | 22 | 23 | 27 | 31 | 35 | 36 | 37 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Smart Cruiter 55 | 56 | 57 |
58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Client/src/Pages/Settings/MainPageOfSetting.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import LeftMenuBar from "../../Components/Dashboard/LeftMenuBar"; 3 | import TopNavigationBar from "../../Components/Dashboard/TopNavigationBar"; 4 | import ProfileIcon from "../../assets/icons/profileIcon.png"; 5 | import TeamIcon from "../../assets/icons/teamIcon.png"; 6 | import TempleteIcon from "../../assets/icons/templeteIcon.png"; 7 | import CareerPage from "../../assets/icons/careerPageIcon.png"; 8 | import { Link } from "react-router-dom"; 9 | function MainPageOfSetting() { 10 | return ( 11 |
12 |
13 | 14 |
15 |
16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 | 25 |

26 | Edit Profile 27 |

28 |
29 | 30 | 31 | {/* //2nd value */} 32 | 33 |
34 | 35 | 36 |

37 | Team Members 38 |

39 |
40 | 41 | {/* 3rd value */} 42 | 43 |
44 | 45 | 46 |

47 | Templetes 48 |

49 |
50 | 51 | {/* 4th value */} 52 | {/* 53 |
54 | 55 | 56 |

57 | Career Page 58 |

59 |
*/} 60 |
61 |
62 |
63 | ); 64 | } 65 | 66 | export default MainPageOfSetting; 67 | -------------------------------------------------------------------------------- /Client/src/Pages/EndUser/PostedJobDescription.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React from "react"; 3 | import { useState } from "react"; 4 | import { useEffect } from "react"; 5 | import { useNavigate, useParams } from "react-router-dom"; 6 | 7 | function PostedJobDescription() { 8 | const { id } = useParams(); 9 | const [details, setDetails] = useState(); 10 | const fetchJobDescription = () => { 11 | // axios POST request 12 | const options = { 13 | url: "https://smart-cruiter-fyp-production.up.railway.app/job/get-jobs/details", 14 | method: "POST", 15 | headers: { 16 | Accept: "application/json", 17 | "Content-Type": "application/json;charset=UTF-8", 18 | }, 19 | data: { id: id }, 20 | }; 21 | 22 | axios(options).then((response) => { 23 | setDetails(response.data.jobs[0]); 24 | }); 25 | }; 26 | useEffect(() => { 27 | fetchJobDescription(); 28 | }, [0]); 29 | console.log(details); 30 | const navigate = useNavigate(); 31 | const handle = () => { 32 | const { org_id, _id } = details; 33 | 34 | navigate(`/portal/job/apply/${_id}`, { 35 | state: { orgID: org_id }, 36 | }); 37 | }; 38 | return ( 39 |
40 |
41 | {/* */} 47 | 48 |

49 | {details?.jobPosition} 50 |

51 |
52 | 53 |
58 |

Job Description

59 |
60 |

61 | Pay : {details?.salaryRangeFrom} ~~ to ~~ {details?.salaryRangeUpto} 62 |

63 |

64 | Totall No Of Jobs : {details?.numberOfSeats} 65 |

66 |

Company : {details?.org_name}

67 |
68 |

72 | 75 |
76 |
77 | ); 78 | } 79 | 80 | export default PostedJobDescription; 81 | -------------------------------------------------------------------------------- /Client/src/Components/Dashboard/CreateJob/CreatedJobElement.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect } from "react"; 3 | import { useState } from "react"; 4 | import { Link, useNavigate } from "react-router-dom"; 5 | import DeleteIcon from "../../../assets/icons/delete.svg"; 6 | import SocialIcon from "../../../assets/icons/share.svg"; 7 | function CreatedJobElement({ data, setData }) { 8 | const navigate = useNavigate(); 9 | const handleJob = (id) => { 10 | navigate(`/JobDetails/${id}`); 11 | }; 12 | // console.log(data); 13 | return ( 14 |
15 | {data?.map((e, index) => { 16 | return ( 17 |
handleJob(e._id)} 20 | title="Job" 21 | className="bg-white hover:bg-gray-100 hover:border hover:border-solid hover:border-gray-300 flex flex-wrap items-center w-80 pl-4 pr-4 pt-2 modalShadow cursor-pointer " 22 | > 23 | {/* */} 24 |
25 |

{e.jobPosition}

26 | 32 |
33 | 34 | {/* PART TO HANDLE DATA */} 35 |
36 |
37 |
Totall Candidates
38 |
{e.applicants_no}
39 |
40 | 41 |
42 |
Active Candidates
43 |
0
44 |
45 |
46 | 47 | {/* PART TO SHOW SHARE JOB-ID AND SHARE BUTTONS */} 48 | 49 |
50 |
51 |

JOB-ID: {index}

52 |
53 | 54 |
55 | 56 | 57 |
58 |
59 | {/* */} 60 |
61 | ); 62 | })} 63 |
64 | ); 65 | } 66 | 67 | export default CreatedJobElement; 68 | -------------------------------------------------------------------------------- /Client/src/assets/icons/share.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Server/Models/JobModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const jobSchema = new mongoose.Schema({ 4 | jobPosition: { 5 | type: String, 6 | required: true, 7 | }, 8 | officeLocation: { 9 | type: String, 10 | required: true, 11 | }, 12 | department: { 13 | type: String, 14 | required: true, 15 | }, 16 | 17 | jobType: { 18 | type: String, 19 | enum: ["Full Time", "Part Time", "Remote Based", "Project Based", "Hourly"], 20 | required: true, 21 | }, 22 | numberOfSeats: { 23 | type: Number, 24 | min: 1, 25 | required: true, 26 | }, 27 | salaryRangeFrom: { 28 | type: Number, 29 | required: true, 30 | }, 31 | salaryRangeUpto: { 32 | type: Number, 33 | required: true, 34 | }, 35 | job_description: { 36 | type: String, 37 | required: true, 38 | }, 39 | city: { 40 | type: String, 41 | required: true, 42 | }, 43 | country: { 44 | type: String, 45 | required: true, 46 | }, 47 | org_name: { 48 | type: String, 49 | required: true, 50 | }, 51 | org_id: { 52 | type: String, 53 | required: true, 54 | }, 55 | applicants_no: { 56 | type: Number, 57 | required: true, 58 | default: 0 59 | }, 60 | 61 | job_status: { 62 | type: String, 63 | enum: ["Active", "Closed"], 64 | default: 'Active' 65 | }, 66 | report_status: { 67 | type: { 68 | applied: Number, 69 | hired: Number, 70 | rejected: Number, 71 | withdrawn: Number, 72 | }, 73 | default: { 74 | applied: 0, 75 | hired: 0, 76 | rejected: 0, 77 | withdraw: 0, 78 | }, 79 | }, 80 | report_experience: { 81 | type: { 82 | nill: Number, 83 | oneyear: Number, 84 | two_to_three: Number, 85 | four_to_five: Number, 86 | five_plus: Number, 87 | }, 88 | default: { 89 | nill: 0, 90 | oneyear: 0, 91 | two_to_three: 0, 92 | four_to_five: 0, 93 | five_plus: 0, 94 | }, 95 | }, 96 | report_educational_level: { 97 | type: [String] 98 | }, 99 | report_city: { 100 | type: [String] 101 | }, 102 | report_university: { 103 | type: [String] 104 | }, 105 | report_male_vs_female: { 106 | type: { 107 | male: Number, 108 | female: Number, 109 | }, 110 | default: { 111 | male: 0, 112 | female: 0, 113 | 114 | }, 115 | }, 116 | 117 | 118 | 119 | 120 | }); 121 | 122 | const Job = mongoose.model("Job", jobSchema); 123 | 124 | module.exports = Job; 125 | -------------------------------------------------------------------------------- /Server/Models/Candidate.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const CandidateSchema = new mongoose.Schema({ 4 | firstName: { 5 | type: String, 6 | required: true, 7 | }, 8 | 9 | lastName: { 10 | type: String, 11 | required: true, 12 | }, 13 | dob: { 14 | type: String, 15 | require: true 16 | }, 17 | gender: { 18 | type: String, 19 | enum: ["Male", "Female"], 20 | required: true, 21 | }, 22 | address: { 23 | type: String, 24 | required: true, 25 | }, 26 | 27 | city: { 28 | type: String, 29 | required: true, 30 | }, 31 | 32 | zipCode: { 33 | type: Number, 34 | required: true, 35 | }, 36 | 37 | institute: [{ 38 | type: String, 39 | required: true, 40 | }], 41 | 42 | level: [{ 43 | type: String, 44 | required: true, 45 | }], 46 | 47 | session: [{ 48 | type: Array, 49 | required: true 50 | }], 51 | majors: [{ 52 | type: String, 53 | required: true, 54 | }], 55 | 56 | 57 | title: [{ 58 | type: String, 59 | required: true, 60 | }], 61 | duration: [{ 62 | type: Number, 63 | required: true, 64 | }], 65 | companyName: [{ 66 | type: String, 67 | required: true, 68 | }], 69 | emailAddress: [{ 70 | type: String, 71 | required: true, 72 | }], 73 | phoneNo: { 74 | type: Number, 75 | required: true, 76 | }, 77 | linkedinProfile: { 78 | type: String, 79 | required: true, 80 | }, 81 | gitHubProfile: { 82 | type: String, 83 | required: true, 84 | }, 85 | 86 | profilePic: { 87 | type: String, 88 | required: true, 89 | }, 90 | 91 | 92 | ResumeURL: { 93 | type: String, 94 | required: true, 95 | } 96 | 97 | , 98 | 99 | jobID: { 100 | type: String, 101 | required: true, 102 | } 103 | , 104 | orgID: { 105 | type: String, 106 | required: true, 107 | }, 108 | recruitmentCycle: { 109 | type: String, 110 | enum: ["Applied", "Interviewing", "Reccomended", "Hired", "Withdrawn"], 111 | default: "Applied" 112 | }, 113 | interviewDate: { 114 | type: String, 115 | default: 'nill' 116 | }, 117 | interviewTime: { 118 | type: String, 119 | default: 'nill' 120 | }, 121 | rating: { 122 | type: Number, 123 | default: 0 124 | }, 125 | interview_link: { 126 | type: String, 127 | default: 'http:zoom.meet.com/783' 128 | }, 129 | feedback_form: { 130 | type: [Number], 131 | default: [0] 132 | }, 133 | 134 | withdrawn_reason: { 135 | type: String, 136 | default: "Try again!" 137 | } 138 | }) 139 | 140 | 141 | const Candidate = mongoose.model("Candidate", CandidateSchema); 142 | 143 | module.exports = Candidate; 144 | -------------------------------------------------------------------------------- /Client/src/Components/Dashboard/CreateJob/AppliedApplicantProfile.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import ShowMoreIcon from "../../../assets/icons/show_more.svg"; 4 | function AppliedApplicantProfile({ id }) { 5 | // const [candidates, setCandidates] = useState(); 6 | // useEffect(() => { 7 | // const fetchData = () => { 8 | // // axios POST request 9 | // const options = { 10 | // url: "http://localhost:3000/job/get-posted-job-details", 11 | // method: "POST", 12 | // headers: { 13 | // Accept: "application/json", 14 | // "Content-Type": "application/json;charset=UTF-8", 15 | // }, 16 | // data: { 17 | // job_id: id, 18 | // }, 19 | // }; 20 | 21 | // axios(options).then((response) => { 22 | // setCandidates(response.data); 23 | // }); 24 | // }; 25 | 26 | // fetchData(); 27 | // }, [0]); 28 | // console.log(candidates); 29 | 30 | return ( 31 |
32 |
33 | {/* CANIDATE PROFILE PICTURE */} 34 | 35 |
36 | 42 |
43 | {/* EDUCATION , CITY AND EXPERINCE STAT UI */} 44 |
45 | {/* NAME FIELD */} 46 |
47 |

Alex Bhaati

48 |
49 |
50 |
51 |
52 |

Experience

53 |
54 |
55 |

1/Year

56 |
57 |
58 | {/* EDUCATION STAT */} 59 |
60 |
61 |

Education

62 |
63 |
64 |

Data Science

65 |
66 |
67 | {/* CITY STAT */} 68 |
69 |
70 |

City

71 |
72 |
73 |

Attock

74 |
75 |
76 |
77 | 80 | 81 | 87 |
88 |
89 |
90 |
91 |
92 | ); 93 | } 94 | 95 | export default AppliedApplicantProfile; 96 | -------------------------------------------------------------------------------- /Client/src/Components/HiredCandidatePage/MainPage.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | 5 | function MainPage() { 6 | const [createdJobs, setCreatedJobs] = useState(); 7 | 8 | useEffect(() => { 9 | const fetchData = () => { 10 | // axios POST request 11 | const options = { 12 | url: "https://smart-cruiter-fyp-production.up.railway.app/job/get-jobs", 13 | method: "POST", 14 | headers: { 15 | Accept: "application/json", 16 | "Content-Type": "application/json;charset=UTF-8", 17 | }, 18 | data: { id: localStorage.getItem("organization_id") }, 19 | }; 20 | 21 | axios(options).then((response) => { 22 | setCreatedJobs(response.data.jobs); 23 | }); 24 | }; 25 | 26 | fetchData(); 27 | }, [0]); 28 | 29 | const navigate = useNavigate(); 30 | return ( 31 |
32 |

33 | Hired Candidate Details 34 |

35 |
36 | {createdJobs?.map((element, index) => { 37 | return ( 38 |
navigate(`details/${element._id}`)} 40 | key={index} 41 | className="cursor-pointer bg-white border border-solid border-gray-200 shadow-md rounded-lg w-80 h-72 pb-2 42 | hover:bg-gray-50 43 | " 44 | > 45 |
46 |
47 |

48 | {element.jobPosition} 49 |

50 |
51 |
52 |

53 | {element.department} 54 |

55 |
56 |
57 | 58 |
59 | {element.department == "HR" ? ( 60 | 65 | ) : element.department == "IT" ? ( 66 | 71 | ) : ( 72 | 77 | )} 78 | 79 |

80 | {element.applicants_no} Applied 81 |

82 |
83 |
84 | ); 85 | })} 86 |
87 |
88 | ); 89 | } 90 | 91 | export default MainPage; 92 | -------------------------------------------------------------------------------- /Client/src/Pages/EndUser/postedJobs.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import Banner from "../../assets/illustrations/job.png"; 5 | function PostedJobs() { 6 | const [data, setData] = useState(); 7 | 8 | useEffect(() => { 9 | const fetchData = async () => { 10 | const response = await axios.get( 11 | "https://smart-cruiter-fyp-production.up.railway.app/job/get-all-jobs" 12 | ); 13 | setData(response.data.fetchAllPostedJobs); 14 | }; 15 | 16 | fetchData(); 17 | }, [0]); 18 | 19 | const navigate = useNavigate(); 20 | const handleMe = (id) => {}; 21 | // console.log(data); 22 | return ( 23 |
24 |
25 |
26 |

Find Your

27 |

Dream

28 |

Job

29 |

,

30 |

31 | Simply fill form and 32 |

33 |

39 | Get Hired 40 |

41 |
42 |
43 | 48 |
49 |
50 |
51 |

52 | All posted jobs 53 |

54 | 55 |
56 | {data?.map((e, index) => { 57 | return ( 58 | <> 59 |
62 | handleMe(navigate(`/portal/job/description/${e._id}`)) 63 | } 64 | className="cursor-pointer w-52 rounded-lg text-center flex items-center h-52 bg-white bg-opacity-20 shadow-sm shadow-gray-900 65 | hover:bg-gray-700 hover:border hover:border-solid border-gray-800 " 66 | > 67 |

68 | {e.jobPosition} 69 |

70 | {/*
71 |

{e.org_name}

72 |
*/} 73 |
74 | 75 | ); 76 | })} 77 |
78 |
79 | 80 |
81 | Powerd By :

Smart Cruiter

82 |
83 |
84 | ); 85 | } 86 | 87 | export default PostedJobs; 88 | -------------------------------------------------------------------------------- /Server/Routes/RecruitmentCycle.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const GetOrganizationPostedJobApplicants = require('../Controllers/Jobs/GetOrganizationPostedJob'); 3 | const DeleteCandidateProfile = require('../Controllers/Recruitment Cycle/DeleteCandidateProfile'); 4 | const FilterCandidates = require('../Controllers/Recruitment Cycle/FilterCandidates'); 5 | const GetCandidateDetails = require('../Controllers/Recruitment Cycle/GetCandidateDetails'); 6 | const GetComments = require('../Controllers/Recruitment Cycle/GetComments'); 7 | const GetHiredCandidate = require('../Controllers/Recruitment Cycle/GetHiredCandidate'); 8 | const GetReccomendedCandidatesDetails = require('../Controllers/Recruitment Cycle/GetReccomendedCandidateDetails'); 9 | const GetReccomendedCandidates = require('../Controllers/Recruitment Cycle/GetReccomendedCandidates'); 10 | const GetWithdrawnCandidate = require('../Controllers/Recruitment Cycle/GetWithdrawnCandidate'); 11 | const GetWithdrawnCandidateDetails = require('../Controllers/Recruitment Cycle/GetWithdrawnCandidateDetails'); 12 | const HandleComments = require('../Controllers/Recruitment Cycle/HandleComments'); 13 | const PatchComments = require('../Controllers/Recruitment Cycle/PatchComments'); 14 | const SaveInterviewDateAndTime = require('../Controllers/Recruitment Cycle/SaveInterviewDateAndTime'); 15 | const SendInterviewEmail = require('../Controllers/Recruitment Cycle/SendInterviewEmail'); 16 | const SentHiredEmail = require('../Controllers/Recruitment Cycle/SentHiredEmail'); 17 | const showActiveCandidateDetails = require('../Controllers/Recruitment Cycle/ShowActiveCandidateDetails'); 18 | const ShowInterviewingCandidate = require('../Controllers/Recruitment Cycle/ShowInterviewingCandidate'); 19 | const SubmitFeedback = require('../Controllers/Recruitment Cycle/SubmitFeedbacl'); 20 | const UpdateStatus = require('../Controllers/Recruitment Cycle/UpdateStatus'); 21 | const UpdateWithdrawnReason = require('../Controllers/Recruitment Cycle/UpdateWithdrawnReason'); 22 | 23 | const RecruitmentRouter = express.Router(); 24 | 25 | 26 | RecruitmentRouter.post("/active/applied", GetOrganizationPostedJobApplicants) 27 | RecruitmentRouter.post("/active/user", showActiveCandidateDetails); 28 | RecruitmentRouter.post("/active/user/filter", FilterCandidates); 29 | RecruitmentRouter.delete("/active/user/delete", DeleteCandidateProfile); 30 | RecruitmentRouter.post("/active/user/add/comments", HandleComments); 31 | RecruitmentRouter.patch("/active/user/patch/comments", PatchComments); 32 | RecruitmentRouter.post("/active/user/get/comments", GetComments); 33 | RecruitmentRouter.post("/active/user/updateStatus", UpdateStatus) 34 | RecruitmentRouter.post("/active/interviewing", ShowInterviewingCandidate); 35 | RecruitmentRouter.post("/active/interviewing/details", GetCandidateDetails); 36 | RecruitmentRouter.post("/active/interviewing/details/sendInterviewEmail", SendInterviewEmail); 37 | RecruitmentRouter.post("/active/interviewing/details/dateandtime", SaveInterviewDateAndTime); 38 | RecruitmentRouter.post("/active/interviewing/details/savefeedback", SubmitFeedback); 39 | RecruitmentRouter.post("/active/reccomended", GetReccomendedCandidates); 40 | RecruitmentRouter.post("/active/reccomended/details", GetReccomendedCandidatesDetails); 41 | RecruitmentRouter.post("/active/hired", GetHiredCandidate); 42 | RecruitmentRouter.post("/active/hired/sendEmail", SentHiredEmail); 43 | 44 | RecruitmentRouter.post("/active/rejected", GetReccomendedCandidates); 45 | RecruitmentRouter.post("/active/withdrawn", GetWithdrawnCandidate); 46 | 47 | RecruitmentRouter.post("/active/withdrawn/details", GetWithdrawnCandidateDetails); 48 | RecruitmentRouter.post("/active/withdrawn/details/updateReason", UpdateWithdrawnReason); 49 | 50 | 51 | module.exports = RecruitmentRouter -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/SentHiredEmail.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Job = require('../../Models/JobModel'); 3 | const app = express(); 4 | const nodemailer = require("nodemailer"); 5 | 6 | const SentHiredEmail = async (req, res, next) => { 7 | 8 | var org_name, job_title; 9 | 10 | const { email_to } = req.body; 11 | const { jobInfo } = req.body; 12 | 13 | 14 | // ************************ 15 | 16 | // STEP : 1 FIND JOB 17 | 18 | // ************************ 19 | const id = jobInfo.job_id; 20 | 21 | if (jobInfo == null) { 22 | res.status(404).json({ message: "ID isn't found in this request" }) 23 | } 24 | const findJob = await Job.findById(id); 25 | 26 | if (findJob) { 27 | org_name = findJob.org_name, 28 | job_title = findJob.jobPosition 29 | } 30 | else { 31 | res.status(404).json({ message: "No job found with this ID" }) 32 | } 33 | 34 | // ********************************* 35 | 36 | // STEP : 2 GET ALL EMAIL - LIST , Email Body and Subject 37 | 38 | // AND SENT EMAIL 39 | 40 | // ********************************* 41 | 42 | const { emailTitle, description } = req.body; 43 | 44 | const SendHireMail = async () => { 45 | 46 | const htmlCode = ` 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 |

59 | ${description} 60 |

61 |
62 |
63 |
Powered By: Smart Cruiter
64 | 65 | 66 | 67 | ` 68 | try { 69 | 70 | const transporter = nodemailer.createTransport({ 71 | host: 'smtp.gmail.com', 72 | port: 587, 73 | secure: false, 74 | auth: { 75 | user: process.env.MAILUSER, 76 | pass: process.env.MAILPASS, 77 | }, 78 | }); 79 | 80 | const mailOptions = { 81 | from: process.env.MAILUSER, 82 | to: [email_to], 83 | subject: emailTitle, 84 | html: htmlCode, 85 | }; 86 | 87 | transporter.sendMail(mailOptions, (error, info) => { 88 | if (error) { 89 | // console.log(error); 90 | return res.status(500).json({ message: "An error occured" }) 91 | } else { 92 | return res.status(200).json({ message: "Email Sent" }) 93 | // console.log(`Email sent: ${info.response}`); 94 | } 95 | }); 96 | 97 | } catch (error) { 98 | return res.status(500).json({ message: "Something goes unexpected , please try again" }) 99 | // console.log("Error -> " + error); 100 | } 101 | } 102 | 103 | 104 | SendHireMail(); 105 | 106 | } 107 | 108 | module.exports = SentHiredEmail; -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/SentDeclinedEmail.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const Job = require('../../Models/JobModel'); 3 | const app = express(); 4 | const nodemailer = require("nodemailer"); 5 | 6 | const SentDeclinedEmail = async (req, res, next) => { 7 | 8 | var org_name, job_title; 9 | 10 | const { email_to } = req.body; 11 | const { jobInfo } = req.body; 12 | 13 | 14 | // ************************ 15 | 16 | // STEP : 1 FIND JOB 17 | 18 | // ************************ 19 | const id = jobInfo.job_id; 20 | 21 | if (jobInfo == null) { 22 | res.status(404).json({ message: "ID isn't found in this request" }) 23 | } 24 | const findJob = await Job.findById(id); 25 | 26 | if (findJob) { 27 | org_name = findJob.org_name, 28 | job_title = findJob.jobPosition 29 | } 30 | else { 31 | res.status(404).json({ message: "No job found with this ID" }) 32 | } 33 | 34 | // ********************************* 35 | 36 | // STEP : 2 GET ALL EMAIL - LIST , Email Body and Subject 37 | 38 | // AND SENT EMAIL 39 | 40 | // ********************************* 41 | 42 | const { emailTitle, description } = req.body; 43 | 44 | const SendHireMail = async () => { 45 | 46 | const htmlCode = ` 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
57 |
58 |

59 | ${description} 60 |

61 |
62 |
63 |
Powered By: Smart Cruiter
64 | 65 | 66 | 67 | ` 68 | try { 69 | 70 | const transporter = nodemailer.createTransport({ 71 | host: 'smtp.gmail.com', 72 | port: 587, 73 | secure: false, 74 | auth: { 75 | user: process.env.MAILUSER, 76 | pass: process.env.MAILPASS, 77 | }, 78 | }); 79 | 80 | const mailOptions = { 81 | from: process.env.MAILUSER, 82 | to: [email_to], 83 | subject: emailTitle, 84 | html: htmlCode, 85 | }; 86 | 87 | transporter.sendMail(mailOptions, (error, info) => { 88 | if (error) { 89 | // console.log(error); 90 | return res.status(500).json({ message: "An error occured" }) 91 | } else { 92 | return res.status(200).json({ message: "Email Sent" }) 93 | // console.log(`Email sent: ${info.response}`); 94 | } 95 | }); 96 | 97 | } catch (error) { 98 | return res.status(500).json({ message: "Something goes unexpected , please try again" }) 99 | // console.log("Error -> " + error); 100 | } 101 | } 102 | 103 | 104 | SendHireMail(); 105 | 106 | } 107 | 108 | module.exports = SentDeclinedEmail; -------------------------------------------------------------------------------- /Client/src/Pages/Auth/ForgetPassword.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useFormik } from "formik"; 3 | import React, { useState } from "react"; 4 | import { useNavigate } from "react-router-dom"; 5 | import { object, string } from "yup"; 6 | import MainButton from "../../Components/Common/MainButton"; 7 | 8 | function ForgetPassword() { 9 | const [error, Seterror] = useState(); 10 | 11 | const navigate = useNavigate(); 12 | const emailValue = { 13 | email: "", 14 | }; 15 | const emailSchema = object({ 16 | email: string().email("*Follow format").required("*Email is must"), 17 | }); 18 | 19 | // -> handle login api call 20 | const handleLogin = async (inputData) => { 21 | console.log(" i am going to run"); 22 | const options = { 23 | url: "https://smart-cruiter-fyp-production.up.railway.app/forget-password", 24 | method: "POST", 25 | headers: { 26 | Accept: "application/json", 27 | "Content-Type": "application/json;charset=UTF-8", 28 | }, 29 | data: inputData, 30 | }; 31 | 32 | axios(options) 33 | .then((response) => { 34 | if (response.status == 200) { 35 | console.log(200); 36 | 37 | navigate("/verifyotp?email=" + inputData.email); 38 | } 39 | }) 40 | .catch(function (error) { 41 | if (error.response.status == 400) { 42 | Seterror("Email address is required"); 43 | } else if (error.response.status == 404) { 44 | Seterror("Email address not found"); 45 | } else if (error.response.status == 401) { 46 | Seterror("Email address is not activated"); 47 | } else { 48 | Seterror("Error processing password reset request"); 49 | } 50 | }); 51 | }; 52 | const formik = useFormik({ 53 | initialValues: emailValue, 54 | validationSchema: emailSchema, 55 | onSubmit: (e) => { 56 | handleLogin(e); 57 | // e.preventDefault(); 58 | // console.log(e); 59 | }, 60 | }); 61 | // console.log(error); 62 | return ( 63 |
64 |
65 | 72 |

73 | Trouble Logging In 74 |

75 | 76 |

77 | Enter your email and we’ll send you a link to get back into your 78 | account. 79 |

80 | 81 |
85 | 86 | 96 | {/* ERROR MSG */} 97 | {formik.errors.email && formik.touched.email ? ( 98 | {formik.errors.email} 99 | ) : null} 100 |
101 | 102 |
103 |
104 | 105 | 106 | Return to Login 107 | 108 |
109 |
110 | ); 111 | } 112 | 113 | export default ForgetPassword; 114 | -------------------------------------------------------------------------------- /Client/src/Components/RecruitmentStage/DeleteCandidateProfileButton.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React from "react"; 3 | import { useState } from "react"; 4 | import { TiUserDelete } from "react-icons/ti"; 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | function DeleteCandidateProfileButton({ id }) { 8 | const [showModal, setShowModal] = useState(false); 9 | 10 | const navigate = useNavigate(); 11 | 12 | const handleDeleteButton = async () => { 13 | // axios POST request 14 | const options = { 15 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/user/delete", 16 | method: "DELETE", 17 | headers: { 18 | Accept: "application/json", 19 | "Content-Type": "application/json;charset=UTF-8", 20 | }, 21 | data: { 22 | id: id, 23 | }, 24 | }; 25 | 26 | axios(options) 27 | .then((response) => { 28 | if (response.status == 200) { 29 | setShowModal(false); 30 | navigate("/jobs"); 31 | } 32 | }) 33 | .then((e) => { 34 | console.log(e); 35 | }); 36 | }; 37 | 38 | return ( 39 |
40 | {/* ///////////// */} 41 | 42 | <> 43 | {showModal ? ( 44 | <> 45 |
46 |
47 | {/*content*/} 48 |
49 | {/*header*/} 50 |
51 |

52 | Are you sure to delete this candidate? 53 |

54 |
55 | {/*body*/} 56 | 57 | {/*footer*/} 58 |
59 | 66 | 76 |
77 |
78 |
79 |
80 |
81 | 82 | ) : null} 83 | 84 | 85 | {/* ////////////////////////////////////// */} 86 |
87 | 93 |
94 |
Delete
95 |
96 | ); 97 | } 98 | 99 | export default DeleteCandidateProfileButton; 100 | -------------------------------------------------------------------------------- /Client/src/Components/Dashboard/CreateJob/CreateJobHeadaer.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React from "react"; 3 | import { useState } from "react"; 4 | import { Link } from "react-router-dom"; 5 | import DownImg from "../../../assets/icons/down.svg"; 6 | function CreateJobHeadaer({ setData }) { 7 | const [jobStatus, SetJobStatus] = useState(false); 8 | const [departmentStatus, SetDepartmentStatus] = useState(false); 9 | 10 | const filterShowClosedJobs = () => { 11 | // axios POST request 12 | const options = { 13 | url: "https://smart-cruiter-fyp-production.up.railway.app/job/get-jobs/closed", 14 | method: "POST", 15 | headers: { 16 | Accept: "application/json", 17 | "Content-Type": "application/json;charset=UTF-8", 18 | }, 19 | data: { id: localStorage.getItem("organization_id") }, 20 | }; 21 | 22 | axios(options).then((response) => { 23 | // console.log(response); 24 | setData(response.data.jobs); 25 | }); 26 | }; 27 | const filterShowActiveJobs = () => { 28 | // axios POST request 29 | const options = { 30 | url: "https://smart-cruiter-fyp-production.up.railway.app/job/get-jobs/active", 31 | method: "POST", 32 | headers: { 33 | Accept: "application/json", 34 | "Content-Type": "application/json;charset=UTF-8", 35 | }, 36 | data: { id: localStorage.getItem("organization_id") }, 37 | }; 38 | 39 | axios(options).then((response) => { 40 | // console.log(response); 41 | setData(response.data.jobs); 42 | }); 43 | }; 44 | 45 | return ( 46 |
47 | {/* --> Main Create Job Button */} 48 | 49 |
50 | 51 | 57 | 58 |
59 | 60 | {/* 2nd flex div */} 61 | 62 |
63 | {/* ==> Job Status Button */} 64 | 71 | {jobStatus == true ? ( 72 |
73 | 84 |
85 | ) : null} 86 | 87 | {/* */} 94 | 95 | {/* {departmentStatus == true ? ( 96 |
97 |
    101 |
  • 102 | IT 103 |
  • 104 |
  • 105 | HR 106 |
  • 107 |
  • 108 | Markeeting 109 |
  • 110 |
111 |
112 | ) : null} */} 113 |
114 |
115 | ); 116 | } 117 | 118 | export default CreateJobHeadaer; 119 | -------------------------------------------------------------------------------- /Server/Controllers/Setup Profile/Profile_Setup.js: -------------------------------------------------------------------------------- 1 | const { json } = require('express'); 2 | const express = require('express'); 3 | const app = express(); 4 | const Cloudinary = require('cloudinary'); 5 | const cloud = require("../../Config/Cloudnary.js"); 6 | 7 | const OrganizationModal = require('../../Models/Organization_Model.js'); 8 | const userModel = require('../../Models/User_Model.js'); 9 | const { findOneAndUpdate } = require('../../Models/Organization_Model.js'); 10 | const ProfileRouter = async (req, res, next) => { 11 | // --> ORGANIZATION DEATILS EXTRACTION 12 | 13 | const org_name = req.body.detailed_data.name; 14 | 15 | const phone = req.body.detailed_data.phone; 16 | 17 | const website_link = req.body.detailed_data.website_link; 18 | const logo = req.body.detailed_data.logo_url; 19 | const departments = req.body.detailed_data.departments; 20 | const address = req.body.detailed_data.address; 21 | const city = req.body.detailed_data.city; 22 | const country = req.body.detailed_data.country; 23 | const region = req.body.detailed_data.region; 24 | const fb_link = req.body.detailed_data.fb_link; 25 | const insta_link = req.body.detailed_data.insta_link; 26 | const yt_link = req.body.detailed_data.yt_link; 27 | const linkedIn_link = req.body.detailed_data.linkedin_link; 28 | 29 | // // --> EXTRACTING TEAM DATA 30 | 31 | 32 | 33 | const { name, email, role } = req.body.team_details; 34 | const data = [{ name, email, role }]; 35 | // console.log(data); 36 | var departments2 = [departments] 37 | departments2 = departments2[0].list 38 | 39 | // // -> to get the image url path 40 | // console.log(req.file.path) 41 | 42 | // // -> Storing selected image to cloud 43 | 44 | // const img = await Cloudinary.v2.uploader.upload(req.file.path); 45 | // const img_url = img.secure_url; 46 | 47 | // -> so 1st check is there is valid reg user which is trying to setup org account 48 | const checkUser = await userModel.findById(req.body.userID) 49 | console.log(checkUser) 50 | if (checkUser.org_registered == false) { 51 | 52 | console.log('1st time hai'); 53 | 54 | const org = await new OrganizationModal({ 55 | "username": checkUser.username, 56 | "password": "Hamza123", 57 | "organization_name": org_name, 58 | "phoneNo": phone, 59 | "website": website_link, 60 | "logo": "Temp_URL", 61 | "departments": departments2, 62 | 63 | "office_address": address, 64 | "office_city": city, 65 | "office_country": country, 66 | "fb_url": fb_link, 67 | "linkedIn_url": linkedIn_link, 68 | "insta_url": insta_link, 69 | "yt_url": insta_link, 70 | "team_members": data 71 | }) 72 | 73 | 74 | 75 | // findOneAndUpdate 76 | 77 | try { 78 | 79 | //Now 1st i have to get the acutall id value from _id with this code 80 | 81 | var user_id = org._id; 82 | user_id = user_id.toString(); 83 | console.log(user_id) 84 | const profile = await userModel.findOneAndUpdate( 85 | { _id: req.body.userID }, // replace with the organization ID 86 | { $set: { org_registered: true, org_id: user_id } }, // use $set operator to update the field 87 | { new: true }, // return the updated document 88 | ); 89 | await org.save() 90 | await profile.save(); 91 | console.log(profile) 92 | 93 | 94 | return res.status(200).json({ message: "user saved" }); 95 | 96 | } catch (error) { 97 | // console.log(error) 98 | return res.status(500).json(error) 99 | } 100 | 101 | 102 | } 103 | 104 | //1st Make Sure Is Organization is already registered or not 105 | else if (checkUser.org_registered == true) { 106 | // console.log('2nd time hai'); 107 | // console.log('already organizaion is REGISTERED :-> STATUS = ' + checkUser.org_registered); 108 | return res.status(400).json({ message: "Already Organization Setup Or Fill Employee All Details" }) 109 | } 110 | 111 | 112 | return res.status(404).json({ message: "Invalid username" }) 113 | } 114 | 115 | 116 | 117 | module.exports = ProfileRouter; -------------------------------------------------------------------------------- /Client/src/Components/RecruitmentStage/SwitchStatus.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { AiFillHeart } from "react-icons/ai"; 3 | import DeleteCandidateProfileButton from "./DeleteCandidateProfileButton"; 4 | import CandidateNotes from "./CandidateNotes"; 5 | import { ToastContainer, toast } from "react-toastify"; 6 | import "react-toastify/dist/ReactToastify.css"; 7 | import { useState } from "react"; 8 | import axios from "axios"; 9 | import { useNavigate } from "react-router-dom"; 10 | 11 | function SwitchStatus({ id }) { 12 | const [status, setStatus] = useState(); 13 | const [alert, setAlert] = useState(); 14 | 15 | const navigate = useNavigate(); 16 | const notify = () => 17 | toast.success("Candidate Status Updated", { 18 | position: "top-center", 19 | autoClose: 1000, 20 | hideProgressBar: false, 21 | closeOnClick: true, 22 | pauseOnHover: true, 23 | draggable: true, 24 | progress: undefined, 25 | theme: "light", 26 | }); 27 | 28 | const handleStatus = () => { 29 | // axios POST request 30 | const options = { 31 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/user/updateStatus", 32 | method: "POST", 33 | headers: { 34 | Accept: "application/json", 35 | "Content-Type": "application/json;charset=UTF-8", 36 | }, 37 | data: { id, status }, 38 | }; 39 | 40 | axios(options) 41 | .then((response) => { 42 | if (response.status == 200) { 43 | notify(); 44 | setTimeout(() => { 45 | navigate(-1); 46 | }, 1000); 47 | } else if (response.status == 300) { 48 | alert("Select any value of interview stage"); 49 | } else { 50 | alert("something went wrong , refresh page and try again"); 51 | } 52 | }) 53 | .catch((e) => { 54 | alert("Something went wrong"); 55 | }); 56 | }; 57 | 58 | return ( 59 |
60 | 72 | 73 |
74 |

Switch Status

75 | 90 | 91 | 100 |
101 | 102 |
103 | {/* NOTES BUTTON CODE */} 104 | 105 | 106 | {/* LIKE BUTTON CODE */} 107 | {/*
108 |
109 | 112 |
113 |
Like
114 |
*/} 115 | 116 | {/* DELETE BUTTON CODE */} 117 |
118 | 119 |
120 |
121 |
122 | ); 123 | } 124 | 125 | export default SwitchStatus; 126 | -------------------------------------------------------------------------------- /Client/src/Components/ProfileSetup/ProfileP1.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useState } from "react"; 3 | import { Link, Outlet, useNavigate } from "react-router-dom"; 4 | import NavigationTab from "../Dashboard/ProfileCreation/NavigationTab"; 5 | 6 | function ProfileP1() { 7 | const navigate = useNavigate(); 8 | 9 | const [selectedImage, setSelectedImage] = useState(); 10 | 11 | // --> To Handle Input Fields 12 | const [profileData, SetProfileData] = useState({ 13 | name: "", 14 | phone_no: "", 15 | website: "", 16 | }); 17 | 18 | console.log(selectedImage); 19 | return ( 20 |
21 | {" "} 22 |
23 | 31 |
32 |
33 | 36 | 45 | SetProfileData((oldValue) => ({ 46 | ...oldValue, 47 | name: e.target.value, 48 | })) 49 | } 50 | /> 51 |
52 | 53 |
54 | 57 | 66 | SetProfileData((oldValue) => ({ 67 | ...oldValue, 68 | phone_no: e.target.value, 69 | })) 70 | } 71 | /> 72 |
73 |
74 | 75 |
76 |
77 | 80 | 89 | SetProfileData((oldValue) => ({ 90 | ...oldValue, 91 | website: e.target.value, 92 | })) 93 | } 94 | /> 95 |
96 | 97 |
98 | 101 | 107 | { 111 | setSelectedImage(event.target.files[0]); 112 | }} 113 | type="file" 114 | /> 115 |
116 |
117 | 128 | 129 |
130 | ); 131 | } 132 | 133 | export default ProfileP1; 134 | -------------------------------------------------------------------------------- /Server/Controllers/Recruitment Cycle/FilterCandidates.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const Candidate = require("../../Models/Candidate"); 3 | 4 | const FilterCandidates = async (req, res, next) => { 5 | console.log("running now"); 6 | 7 | const { filter_value } = req.body; 8 | const BSCandidates = await Candidate.find(); 9 | 10 | if (filter_value == "MALE") { 11 | const result = BSCandidates.filter((level) => level.gender == "Male"); 12 | return res.status(200).json(result); 13 | } else if (filter_value == "FEMALE") { 14 | const result = BSCandidates.filter((level) => level.gender == "Female"); 15 | return res.status(200).json(result); 16 | } 17 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | 20 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 | // -> FILTER ON YEARS OF EXPERIENCE 22 | // **there is some issue with experience resolve it back 23 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 24 | 25 | if (filter_value === "Experience" || filter_value.startsWith("Experience")) { 26 | //1st Trim the req.body value because i will be sending input in this format Experience:1 27 | const str = filter_value; 28 | const value = str.split(":")[1].trim(); 29 | //Now checking that city_value in DB 30 | console.log('I AM RUNNING'); 31 | console.log(filter_value); 32 | console.log(value); 33 | if (value == 0) { 34 | const result = BSCandidates.filter((level) => level.duration <= 1); 35 | return res.status(200).json(result); 36 | } else if (value == 25) { 37 | const result = BSCandidates.filter( 38 | (level) => level.duration >= 1 && level.duration <= 3 39 | ); 40 | return res.status(200).json(result); 41 | } else if (value == 50) { 42 | const result = BSCandidates.filter( 43 | (level) => level.duration >= 3 && level.duration <= 5 44 | ); 45 | return res.status(200).json(result); 46 | } else if (value == 75) { 47 | const result = BSCandidates.filter( 48 | (level) => level.duration >= 5 && level.duration <= 8 49 | ); 50 | return res.status(200).json(result); 51 | } else if (value == 100) { 52 | const result = BSCandidates.filter((level) => level.duration >= 8); 53 | return res.status(200).json(result); 54 | } else { 55 | return res.status(404).json({ message: "No user from this city exsist" }); 56 | } 57 | } 58 | 59 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 60 | // -> FILTER ON BASIS OF CITY NAME 61 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 62 | 63 | if (filter_value === "City" || filter_value.startsWith("City")) { 64 | //1st Trim the req.body value because i will be sending input in this format City : city_name 65 | const str = filter_value; 66 | const value = str.split(":")[1].trim(); 67 | //Now checking that city_value in DB 68 | const result = BSCandidates.filter((level) => level.city == value); 69 | if (result) { 70 | return res.status(200).json(result); 71 | } else { 72 | return res.status(404).json({ message: "No user from this city exsist" }); 73 | } 74 | } 75 | 76 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 77 | // -> FILTER ON BASIS OF EDUCATIONAL QUALIFICATION 78 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 79 | else if (filter_value == "BS") { 80 | const result = BSCandidates.filter((level) => level.level.length == 1); 81 | 82 | return res.status(200).json(result); 83 | } else if (filter_value == "MS") { 84 | const result = BSCandidates.filter((level) => level.level.length == 2); 85 | 86 | return res.status(200).json(result); 87 | } else if (filter_value == "PHD") { 88 | const result = BSCandidates.filter((level) => level.level.length == 3); 89 | 90 | return res.status(200).json(result); 91 | } 92 | 93 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 94 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 95 | else { 96 | return res.status(404).json({ message: "No candidate found" }); 97 | } 98 | }; 99 | 100 | module.exports = FilterCandidates; 101 | -------------------------------------------------------------------------------- /Client/src/Components/RecruitmentStage/ReccomendidCandidateCard.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { useNavigate } from "react-router-dom"; 4 | import NoUserSVG from "../../assets/illustrations/no_user.svg"; 5 | 6 | function ReccomendidCandidateCard({ id }) { 7 | const [candidate, setCandidate] = useState(); 8 | useEffect(() => { 9 | const getCandidates = () => { 10 | const options = { 11 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/reccomended", 12 | method: "POST", 13 | headers: { 14 | Accept: "application/json", 15 | "Content-Type": "application/json;charset=UTF-8", 16 | }, 17 | data: { id }, 18 | }; 19 | 20 | axios(options).then((response) => { 21 | if (response.status == 200) { 22 | setCandidate(response.data); 23 | } else { 24 | alert("something went wrong , try again"); 25 | } 26 | }); 27 | }; 28 | 29 | getCandidates(); 30 | }, [0]); 31 | 32 | const navigate = useNavigate(); 33 | 34 | return ( 35 |
36 | {candidate?.length !== 0 ? ( 37 | candidate?.map((e, index) => { 38 | var educationLevelLastValue = e?.level.slice(-1)[0]; 39 | 40 | return ( 41 |
44 | navigate(`/JobDetails/reccomended/details/${e._id}`) 45 | } 46 | className="w-4/5 mt-6 block m-auto bg-white h-auto p-5 shadow-md rounded-md hover:bg-gray-50 hover:border border-solid border-gray-300 cursor-pointer " 47 | > 48 |
49 |
50 | 56 |
57 | {/* 2nd Profile Info */} 58 |
59 |

60 | {e.firstName + " " + e.lastName} 61 |

62 | 63 |
64 |
65 |
66 |

Experience

67 |

{e.duration}

68 |
69 |
70 | 71 |
72 |
73 |

Education

74 |

{educationLevelLastValue}

75 |
76 |
77 | 78 |
79 |
80 |

City

81 |

{e.city}

82 |
83 |
84 | 85 |
86 |
87 |

88 | Interview Date 89 |

90 |

{e.interviewDate}

91 |
92 |
93 |
94 |
95 | 96 | {/*
97 |

{e.}%

98 |
*/} 99 |
100 |
101 | ); 102 | }) 103 | ) : ( 104 | // ********************************************* 105 | // CODE FOR IF THERE ARE NOT RECCOMENDED USER 106 | // ********************************************* 107 |
108 | 109 | 110 |

111 | Currently no Interviewing User 112 |

113 |
114 | )} 115 |
116 | ); 117 | } 118 | 119 | export default ReccomendidCandidateCard; 120 | -------------------------------------------------------------------------------- /Client/src/Components/ProfileSetup/Profile_Social3.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import NavigationTab from "../Dashboard/ProfileCreation/NavigationTab"; 3 | import { 4 | FaFacebookF, 5 | FaLinkedinIn, 6 | FaInstagram, 7 | FaYoutube, 8 | } from "react-icons/fa"; 9 | import { useNavigate, useLocation } from "react-router-dom"; 10 | import { useState } from "react"; 11 | 12 | function Profile_Social3() { 13 | const location = useLocation(); 14 | // const { basicInfo, image } = location.state; 15 | console.log(location.state); 16 | 17 | // => A new object which can handle old + new value to pass to the next component using useNavigate() 18 | const [socialDetails, setSocialDetails] = useState({ 19 | facebook_url: "", 20 | insta_url: "", 21 | linkedin_url: "asd", 22 | yt_url: "dsa", 23 | }); 24 | console.log(socialDetails); 25 | 26 | //An data object to pass it to the next Route 27 | const Office_Profile = { 28 | office_details: location.state, 29 | social_links: socialDetails, 30 | }; 31 | const navigate = useNavigate(); 32 | return ( 33 |
34 | {" "} 35 |
36 | 44 | {/* THIS MENUE MAIN INPUT CONTENT */} 45 |
46 |

Add Social Link's

47 | 48 | {/* HANDLING SOCIAL INPUT */} 49 | 50 |
51 |
52 | 53 | 58 | setSocialDetails((oldValue) => ({ 59 | ...oldValue, 60 | facebook_url: e.target.value, 61 | })) 62 | } 63 | placeholder="Facebook Profile" 64 | autoComplete="on" 65 | className="input input-bordered h-10 w-full max-w-xs inline" 66 | /> 67 |
68 | 69 |
70 | 71 | 76 | setSocialDetails((old) => ({ 77 | ...old, 78 | linkedin_url: e.target.value, 79 | })) 80 | } 81 | placeholder="LinkedIn" 82 | autoComplete="on" 83 | className="input input-bordered h-10 w-full max-w-xs inline" 84 | /> 85 |
86 |
87 | 88 | {/* HERE IS THE 2ND LINES OF SOCIAL LINK UI CODE */} 89 | 90 |
91 |
92 | 93 | 98 | setSocialDetails((old) => ({ 99 | ...old, 100 | insta_url: e.target.value, 101 | })) 102 | } 103 | placeholder="Insta Page" 104 | autoComplete="on" 105 | className="input input-bordered h-10 w-full max-w-xs inline" 106 | /> 107 |
108 | 109 |
110 | 111 | 116 | setSocialDetails((old) => ({ 117 | ...old, 118 | yt_url: e.target.value, 119 | })) 120 | } 121 | placeholder="YouTube Channel" 122 | autoComplete="on" 123 | className="input input-bordered h-10 w-full max-w-xs inline" 124 | /> 125 |
126 |
127 |
128 | 137 |
138 |
139 | ); 140 | } 141 | 142 | export default Profile_Social3; 143 | -------------------------------------------------------------------------------- /Client/src/Components/RecruitmentStage/WithdrawnDetailsCard.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect } from "react"; 3 | import { useState } from "react"; 4 | import ReactQuill from "react-quill"; 5 | import { toast, ToastContainer } from "react-toastify"; 6 | import SwitchStatus from "./SwitchStatus"; 7 | 8 | function WithdrawnDetailsCard({ id }) { 9 | const [userDetails, setUserDetails] = useState(); 10 | const [description, setDescription] = useState(); 11 | 12 | const notify = () => 13 | toast.success("Updated", { 14 | position: "top-center", 15 | autoClose: 1000, 16 | hideProgressBar: false, 17 | closeOnClick: true, 18 | pauseOnHover: true, 19 | draggable: true, 20 | progress: undefined, 21 | theme: "light", 22 | }); 23 | 24 | useEffect(() => { 25 | // axios POST request 26 | const options = { 27 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/withdrawn/details", 28 | method: "POST", 29 | headers: { 30 | Accept: "application/json", 31 | "Content-Type": "application/json;charset=UTF-8", 32 | }, 33 | data: { id }, 34 | }; 35 | 36 | axios(options) 37 | .then((response) => { 38 | if (response.status == 200) { 39 | setUserDetails(response.data); 40 | setDescription(response.data.withdrawn_reason); 41 | } else { 42 | alert("Something went wrong, refresh page and try again"); 43 | } 44 | }) 45 | .catch((e) => { 46 | alert("Something went wrong, refresh page and try again"); 47 | }); 48 | }, [0]); 49 | 50 | const handleTextValue = () => { 51 | // axios POST request 52 | const options = { 53 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/withdrawn/details/updateReason", 54 | method: "POST", 55 | headers: { 56 | Accept: "application/json", 57 | "Content-Type": "application/json;charset=UTF-8", 58 | }, 59 | data: { id, description }, 60 | }; 61 | 62 | axios(options) 63 | .then((response) => { 64 | if (response.status == 200) { 65 | notify(); 66 | console.log(response); 67 | } else { 68 | alert("Something went wrong, refresh page and try again"); 69 | } 70 | }) 71 | .catch((e) => { 72 | alert("Something went wrong, refresh page and try again"); 73 | }); 74 | }; 75 | 76 | return ( 77 |
78 |

Withdrawn Candidate

79 | 80 | 92 | 93 |
94 |
95 | 103 | 104 |

105 | {userDetails?.firstName + " " + userDetails?.lastName} 106 |

107 |
108 | 109 |
110 | 113 | 114 | {/* ********************************** 115 | MODAL UI CODE 116 | ********************************** */} 117 | 118 | 119 |
120 |
121 | 127 | 132 |
133 |
134 | {/* ***************************** 135 | ***************************** */} 136 |
137 |
138 | 139 |
140 |

Widthdrawn Reason

141 | 142 | 147 | 148 | 154 |
155 |
156 | ); 157 | } 158 | 159 | export default WithdrawnDetailsCard; 160 | -------------------------------------------------------------------------------- /Client/src/Components/RecruitmentStage/WithdrawnCandidateCard.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect, useState } from "react"; 3 | import { BsFillInfoCircleFill } from "react-icons/bs"; 4 | import { motion } from "framer-motion"; 5 | import { FiSend } from "react-icons/fi"; 6 | import ReactQuill from "react-quill"; 7 | import { useNavigate } from "react-router-dom"; 8 | import NoUserSVG from "../../assets/illustrations/no_user.svg"; 9 | import OkSVG from "../../assets/illustrations/done.svg"; 10 | import { BeatLoader } from "react-spinners"; 11 | function WithdrawnCandidateCard({ id }) { 12 | // const { width, height } = useWindowSize(); 13 | 14 | const [candidate, setCandidate] = useState(); 15 | 16 | useEffect(() => { 17 | const getCandidates = async () => { 18 | const options = { 19 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/withdrawn", 20 | method: "POST", 21 | headers: { 22 | Accept: "application/json", 23 | "Content-Type": "application/json;charset=UTF-8", 24 | }, 25 | data: { id }, 26 | }; 27 | 28 | axios(options) 29 | .then((response) => { 30 | console.log(response); 31 | if (response.status == 200) { 32 | setCandidate(response.data); 33 | } else { 34 | alert("something went wrong , try again"); 35 | } 36 | }) 37 | .catch((e) => { 38 | alert("something went wrong , try again"); 39 | }); 40 | }; 41 | 42 | getCandidates(); 43 | }, [0]); 44 | 45 | const navigate = useNavigate(); 46 | 47 | return ( 48 |
49 | {candidate?.length !== 0 ? ( 50 | candidate?.map((e, index) => { 51 | var educationLevelLastValue = e?.level.slice(-1)[0]; 52 | 53 | return ( 54 |
{ 57 | navigate(`/JobDetails/withdrawn/details/${e._id}`); 58 | }} 59 | className="w-4/5 mb-6 block m-auto bg-white h-auto p-5 shadow-md rounded-md hover:bg-gray-50 hover:border border-solid border-gray-300 cursor-pointer " 60 | > 61 |
62 |
63 | 69 |
70 | {/* 2nd Profile Info */} 71 |
72 |

73 | {e.firstName + " " + e.lastName} 74 |

75 | 76 |
77 |
78 |
79 |

Experience

80 |

{e.duration}

81 |
82 |
83 | 84 |
85 |
86 |

Education

87 |

{educationLevelLastValue}

88 |
89 |
90 | 91 |
92 |
93 |

City

94 |

{e.city}

95 |
96 |
97 | 98 |
99 |
100 |

101 | Interview Date 102 |

103 |

{e.interviewDate}

104 |
105 |
106 |
107 |
108 | 109 | {/*
110 |

{e.}%

111 |
*/} 112 |
113 |
114 | ); 115 | }) 116 | ) : ( 117 | // ********************************************* 118 | // CODE FOR IF THERE ARE NOT RECCOMENDED USER 119 | // ********************************************* 120 |
121 | 122 | 123 |

124 | Currently no Withdrawn Candidate 125 |

126 |
127 | )} 128 |
129 | ); 130 | } 131 | 132 | export default WithdrawnCandidateCard; 133 | -------------------------------------------------------------------------------- /Server/Controllers/Report Stats/MainPage.js: -------------------------------------------------------------------------------- 1 | const Job = require("../../Models/JobModel"); 2 | 3 | const StatMainPage = async (req, res) => { 4 | const id = req.body.id; 5 | if (!id) { 6 | res.status(440).json({ message: "No id found" }) 7 | } 8 | 9 | 10 | 11 | const findTotallJobs = await Job.find({ org_id: id }); 12 | 13 | //Totall jobs posted 14 | const totallJobsPosted = findTotallJobs.length; 15 | 16 | // Counting totall number of candidates applied till now 17 | const applicantsCount = findTotallJobs.map((job) => job.applicants_no); 18 | const totallApplicants = applicantsCount.reduce((a, b) => a + b, 0) 19 | 20 | 21 | // Getting the applied , rejected , hired stats 22 | 23 | const reportForJobStats = findTotallJobs.map((job) => job.report_status); 24 | 25 | 26 | // Initialize the sum variables 27 | var hiredSum = 0; 28 | var appliedSum = 0; 29 | 30 | // Loop through the array 31 | for (var i = 0; i < reportForJobStats.length; i++) { 32 | // Access the object at index i 33 | var obj = reportForJobStats[i]; 34 | 35 | // Extract the values for 'hired' and 'applied' keys 36 | var hiredValue = obj.hired; 37 | var appliedValue = obj.applied; 38 | 39 | // Add the values to the sum variables 40 | hiredSum += hiredValue; 41 | appliedSum += appliedValue; 42 | } 43 | 44 | 45 | // Output the sum values 46 | // console.log("Sum of 'hired' key values:", hiredSum); 47 | // console.log("Sum of 'applied' key values:", appliedSum); 48 | 49 | 50 | // ************************************************* 51 | // NOW EXTRACTING EDUCATIONAL QUALIFICATION 52 | // ************************************************* 53 | 54 | const reportForEducationalStatus = findTotallJobs.map((job) => job.report_educational_level); 55 | 56 | 57 | // const array = [[], ['Ph.D', 'Ph.D', 'BS', 'BS']]; 58 | 59 | // Flatten the array 60 | const flattenedArray = reportForEducationalStatus.flat(); 61 | 62 | // Count the occurrences of each value 63 | const countMap = flattenedArray.reduce((acc, value) => { 64 | if (acc[value]) { 65 | acc[value] += 1; 66 | } else { 67 | acc[value] = 1; 68 | } 69 | return acc; 70 | }, {}); 71 | 72 | // Log the counts 73 | // console.log('Number of occurrences for each value:'); 74 | // console.log(countMap); 75 | 76 | 77 | // ************************************************* 78 | // NOW EXTRACTING CITIES OF APPLICANTS 79 | // ************************************************* 80 | 81 | 82 | const reportForCities = findTotallJobs.map((job) => job.report_city); 83 | 84 | // Flatten the array 85 | const flattenedArray2 = reportForCities.flat(); 86 | 87 | // Create an object to store unique values and their counts 88 | const citiesList = {}; 89 | 90 | // Count the occurrences of each value 91 | flattenedArray2.forEach(value => { 92 | if (citiesList[value]) { 93 | citiesList[value] += 1; 94 | } else { 95 | citiesList[value] = 1; 96 | } 97 | }); 98 | 99 | // Log the unique values and their counts 100 | // for (const value in citiesList) { 101 | // console.log(`${value}: ${citiesList[value]}`); 102 | // } 103 | 104 | 105 | 106 | // ************************************************* 107 | // NOW EXTRACTING UNIVERSITIES LIST OF APPLICANTS 108 | // ************************************************* 109 | 110 | 111 | const reportForUniversities = findTotallJobs.map((job) => job.report_university); 112 | 113 | // Flatten the array 114 | const flattenedArray3 = reportForUniversities.flat(); 115 | 116 | // Create an object to store unique values and their counts 117 | const UniversitiesList = {}; 118 | 119 | // Count the occurrences of each value 120 | flattenedArray3.forEach(value => { 121 | if (UniversitiesList[value]) { 122 | UniversitiesList[value] += 1; 123 | } else { 124 | UniversitiesList[value] = 1; 125 | } 126 | }); 127 | 128 | // Log the unique values and their counts 129 | // for (const value in UniversitiesList) { 130 | // console.log(`${value}: ${UniversitiesList[value]}`); 131 | // } 132 | 133 | 134 | // ************************************************* 135 | // NOW EXTRACTING APPLICANTS MALE VS FEMALE RATIO % 136 | // ************************************************* 137 | 138 | const reportForMaleVSFemale = findTotallJobs.map((job) => job.report_male_vs_female); 139 | 140 | 141 | const GenderRatio = reportForMaleVSFemale.reduce((accumulator, currentValue) => { 142 | return { 143 | male: accumulator.male + currentValue.male, 144 | female: accumulator.female + currentValue.female 145 | }; 146 | }, { male: 0, female: 0 }); 147 | 148 | 149 | const total = GenderRatio.male + GenderRatio.female; 150 | 151 | const malePercentage = (GenderRatio.male / total) * 100; 152 | const femalePercentage = (GenderRatio.female / total) * 100; 153 | const GenderPercentage = { 154 | Male: malePercentage, 155 | Female: femalePercentage 156 | } 157 | 158 | 159 | res.send({ hiredSum, appliedSum, totallApplicants, totallJobsPosted, countMap, citiesList, UniversitiesList, GenderPercentage }) 160 | 161 | } 162 | 163 | module.exports = StatMainPage; -------------------------------------------------------------------------------- /Client/src/Components/RecruitmentStage/ReccomendedCandidateDetailsCard.jsx: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import React, { useEffect } from "react"; 3 | import { useState } from "react"; 4 | 5 | import { 6 | FcAssistant, 7 | FcDataSheet, 8 | FcGraduationCap, 9 | FcHome, 10 | FcInvite, 11 | } from "react-icons/fc"; 12 | 13 | function ReccomendedCandidateDetailsCard({ id, user, SetUser, setID }) { 14 | useEffect(() => { 15 | const getCanidateDetails = () => { 16 | // axios POST request 17 | const options = { 18 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/reccomended/details", 19 | method: "POST", 20 | headers: { 21 | Accept: "application/json", 22 | "Content-Type": "application/json;charset=UTF-8", 23 | }, 24 | data: { id }, 25 | }; 26 | 27 | axios(options) 28 | .then((response) => { 29 | SetUser(response.data); 30 | setID(response.data._id); 31 | }) 32 | .catch((e) => { 33 | console.log(e); 34 | }); 35 | }; 36 | 37 | getCanidateDetails(); 38 | }, [0]); 39 | 40 | var educationLevelLastValue = user?.level.slice(-1)[0]; 41 | 42 | const calculateFeebackPercentage = (feedback) => { 43 | let rating = 0; 44 | for (let i = 0; i < feedback?.length; i++) { 45 | if (feedback[i] == 0) { 46 | rating += 0; 47 | } else { 48 | rating += feedback[i] * 4; 49 | } 50 | } 51 | return rating; 52 | }; 53 | const rating = calculateFeebackPercentage(user?.feedback_form); 54 | 55 | return ( 56 |
57 | {/* **************************************************** 58 | THIS IS 1ST DIV SHOWING PROFILE PICTURE AND RATING 59 | **************************************************** */} 60 |
61 | 68 | 74 | 75 | 76 |
77 |
78 | 84 | 91 |
92 |
93 | 94 |

Interview Rating

95 | 96 |
97 |

{rating} %

98 |
99 |

Details Here

100 |
101 | 102 | {/* **************************************************** 103 | THIS IS 2nd DIV SHOWING CANDIDATE DETAILS 104 | **************************************************** */} 105 |
106 |

107 | {user?.firstName + " " + user?.lastName} 108 |

109 | 110 |
111 |
112 |

113 | City : 114 |

115 |

{user?.city}

116 |
117 | 118 |
119 |

120 | DoB 121 | : 122 |

123 |

{user?.dob}

124 |
125 | 126 |
127 |

128 | {" "} 129 | Last Degree :{" "} 130 |

131 |

{educationLevelLastValue}

132 |
133 | 134 |
135 |

136 | {" "} 137 | Interview Date :{" "} 138 |

139 |

{user?.interviewDate}

140 |
141 | 142 |
143 |

144 | Email :{" "} 145 |

146 |

{user?.emailAddress}

147 |
148 |
149 |
150 |
151 | ); 152 | } 153 | 154 | export default ReccomendedCandidateDetailsCard; 155 | -------------------------------------------------------------------------------- /Client/src/Components/Dashboard/CreateJob/FIlterProfiles.jsx: -------------------------------------------------------------------------------- 1 | import React, { useRef, useState } from "react"; 2 | import { Checkbox } from "@chakra-ui/react"; 3 | 4 | import { BsPlusCircle } from "react-icons/bs"; 5 | import axios from "axios"; 6 | import { useEffect } from "react"; 7 | 8 | function FilterProfiles({ can, setCan }) { 9 | const [city, setCity] = useState(); 10 | const [slider, setSlider] = useState(0); 11 | const city_name = useRef(); 12 | const filterCandidates = async (filter) => { 13 | // axios POST request 14 | const options = { 15 | url: "https://smart-cruiter-fyp-production.up.railway.app/details/active/user/filter", 16 | method: "POST", 17 | headers: { 18 | Accept: "application/json", 19 | "Content-Type": "application/json;charset=UTF-8", 20 | }, 21 | data: { 22 | filter_value: filter, 23 | }, 24 | }; 25 | 26 | axios(options) 27 | .then((response) => { 28 | setCan(response.data); 29 | }) 30 | .catch((e) => { 31 | console.log(e); 32 | }); 33 | }; 34 | 35 | return ( 36 |
37 |

Filter Profile

38 | 39 |
40 |

41 | Education 42 |

43 | 44 | {/* Education check boxes */} 45 |
46 | filterCandidates("BS")} 53 | > 54 | BS 55 | 56 | 57 | filterCandidates("MS")} 64 | > 65 | MS 66 | 67 | 68 | filterCandidates("PHD")} 76 | > 77 | Ph.D 78 | 79 |
80 | {/* EXPERIANCE UI */} 81 |

Experience

82 |
83 | { 85 | setSlider(e.target.value); // get value as :25 86 | filterCandidates("Experience:" + slider); //experience : 3s 87 | }} 88 | value={slider} 89 | type="range" 90 | min="0" 91 | max="100" 92 | className="range" 93 | step="25" 94 | /> 95 |
96 | 0|1 97 | 1|3 98 | 3|5 99 | 5|8 100 | 8+ 101 |
{" "} 102 |
103 | 104 | {/* CITY INPUT UI */} 105 | 106 |

City

107 |
108 | (city_name.current = e.target.value)} 114 | /> 115 | 122 |
123 | {/* INSERTED TAGS LIST */} 124 | {city !== null ? ( 125 |
126 |
filterCandidates("City:" + city)} 128 | className="cursor-pointer line1 bg-gray-800 p-2 rounded-lg text-white ml-3" 129 | > 130 | {city} 131 |
132 |
133 | ) : undefined} 134 | 135 | {/* GENDER SELECTION UI */} 136 |

137 | Gender 138 |

139 |
140 | filterCandidates("MALE")} 147 | > 148 | Male 149 | 150 | 151 | filterCandidates("FEMALE")} 158 | > 159 | Female 160 | 161 |
162 |
163 |
164 | ); 165 | } 166 | 167 | export default FilterProfiles; 168 | --------------------------------------------------------------------------------