├── client ├── .env ├── public │ ├── Jobpilot.png │ └── vite.svg ├── src │ ├── images │ │ ├── ring-r.png │ │ ├── seeker.jpg │ │ ├── Jobpilot.png │ │ └── seeker-bg.png │ ├── index.css │ ├── pages │ │ ├── Dashboard.jsx │ │ ├── Poster.Dashboard.jsx │ │ ├── Finish.jsx │ │ ├── NotFound.jsx │ │ ├── Seeker.Dashboard.jsx │ │ ├── ChatList.jsx │ │ ├── Landing.jsx │ │ ├── About.jsx │ │ ├── Post.jsx │ │ ├── Fpost.jsx │ │ └── SignIn.jsx │ ├── components │ │ ├── Benifits.jsx │ │ ├── PrivateRoute.jsx │ │ ├── themeProvider.jsx │ │ ├── Poster.Postjob.jsx │ │ ├── poster.Setting.jsx │ │ ├── Poster.Dashusers.jsx │ │ ├── CreatePostNavBar.jsx │ │ ├── Seeker.DashCart.jsx │ │ ├── PostCards.jsx │ │ ├── JobPostCard.jsx │ │ ├── Seeker.DashSidebar.jsx │ │ ├── SearchCard.jsx │ │ ├── Footer.jsx │ │ ├── AdminCard.jsx │ │ ├── ApplicationForm.jsx │ │ ├── Poster.FoundInfo.jsx │ │ ├── Poster.DashSidebar.jsx │ │ ├── ChatMessage.jsx │ │ ├── Message.jsx │ │ ├── Seeker.CompanyDetailsModal.jsx │ │ ├── Seeker.DashAppliedjobs.jsx │ │ ├── Seeker.DashLatestjobs.jsx │ │ ├── Seeker.DashOverview.jsx │ │ ├── Seeker.Comment.jsx │ │ ├── Header.jsx │ │ ├── Seeker.PartTimeDetailsModel.jsx │ │ ├── Post.PartTimeJobPost.jsx │ │ ├── Poster.SocialMediaInfo.jsx │ │ ├── Poster.DashOverview.jsx │ │ ├── Seeker.cartPost.jsx │ │ └── Post.FulltimeJobPost.jsx │ ├── redux │ │ ├── theme │ │ │ └── themeSlice.js │ │ ├── store.js │ │ └── user │ │ │ └── userSlice.js │ ├── main.jsx │ ├── firebase.js │ └── App.jsx ├── postcss.config.js ├── index.html ├── tailwind.config.js ├── README.md ├── vite.config.js ├── .eslintrc.cjs └── package.json ├── .env ├── api ├── .env ├── utils │ ├── error.js │ └── verifyUser.js ├── routes │ ├── contact.route.js │ ├── savecandidate.js │ ├── auth.route.js │ ├── jobseeker.route.js │ ├── jobposter.route.js │ ├── message.route.js │ ├── comment.route.js │ ├── seeker.route.js │ └── post.route.js ├── controllers │ ├── contact.controller.js │ ├── email.controller.js │ ├── savecandidate.controller.js │ ├── comment.controller.js │ ├── auth.controller.js │ ├── jobseeker.controller.js │ ├── jobposter.controller.js │ ├── messaage.controller.js │ ├── post.controller.js │ └── seeker.controller.js ├── models │ ├── contact.model.js │ ├── comment.model.js │ ├── message.model.js │ ├── savecandidate.js │ ├── post.model.js │ └── user.model.js └── index.js ├── .gitignore ├── package.json ├── LICENSE └── README.md /client/.env: -------------------------------------------------------------------------------- 1 | VITE_FIREBASE_API_KEY = "AIzaSyDMHcrqX6cLMOD7_IS2q4sbgTfhCcUpjrs" -------------------------------------------------------------------------------- /client/public/Jobpilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sameemadhu16/Job-Listings/HEAD/client/public/Jobpilot.png -------------------------------------------------------------------------------- /client/src/images/ring-r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sameemadhu16/Job-Listings/HEAD/client/src/images/ring-r.png -------------------------------------------------------------------------------- /client/src/images/seeker.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sameemadhu16/Job-Listings/HEAD/client/src/images/seeker.jpg -------------------------------------------------------------------------------- /client/src/images/Jobpilot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sameemadhu16/Job-Listings/HEAD/client/src/images/Jobpilot.png -------------------------------------------------------------------------------- /client/src/images/seeker-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sameemadhu16/Job-Listings/HEAD/client/src/images/seeker-bg.png -------------------------------------------------------------------------------- /client/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | 6 | textarea { 7 | outline: none; 8 | border: none; 9 | } -------------------------------------------------------------------------------- /client/src/pages/Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Dashboard() { 4 | return ( 5 |
Dashboard
6 | ) 7 | } 8 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MONGODB = "mongodb+srv://ilakshitha7921:Ishan@123@cluster0.g0kc8rx.mongodb.net/jobwave?retryWrites=true&w=majority&appName=Cluster0" 2 | 3 | JWT_SECRET = 'job_pilot' -------------------------------------------------------------------------------- /api/.env: -------------------------------------------------------------------------------- 1 | 2 | SMTP_HOST = stmp.gmail.com 3 | STMP_PORT = 4 | STMP_MAIL =ilakshitha7921@gmail.com 5 | STMP_PASSSWORD =ebeq vcpa cjgk kiyk 6 | #TO_EMAIL = 7 | PORT = 8000 8 | 9 | 10 | -------------------------------------------------------------------------------- /api/utils/error.js: -------------------------------------------------------------------------------- 1 | //middleware to error handle 2 | export const errorHandler = (status, message) =>{ 3 | const error = new Error(); 4 | error.status = status; 5 | error.message = message; 6 | return error; 7 | } -------------------------------------------------------------------------------- /api/routes/contact.route.js: -------------------------------------------------------------------------------- 1 | 2 | import express from 'express'; 3 | 4 | import sendEmail from '../controllers/email.controller.js'; 5 | 6 | const router = express.Router(); 7 | router.post('/sendmail', sendEmail); 8 | export default router; 9 | -------------------------------------------------------------------------------- /client/src/components/Benifits.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Benifits() { 4 | return ( 5 |
6 |

Benifits

7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /client/src/components/PrivateRoute.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useSelector } from 'react-redux' 3 | import { Outlet,Navigate } from 'react-router-dom' 4 | 5 | export default function PrivateRoute() { 6 | 7 | const {currentUser}= useSelector((state) => state.user) 8 | return currentUser ? : 9 | } 10 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /api/routes/savecandidate.js: -------------------------------------------------------------------------------- 1 | import {applyForJob, allApplications, findApplicant } from "../controllers/savecandidate.controller.js"; 2 | import express from 'express'; 3 | 4 | const router = express.Router(); 5 | 6 | router.post('/apply',applyForJob); 7 | router.get('/allApplicants',allApplications); 8 | router.get('/findApplicants/:userId',findApplicant); 9 | 10 | 11 | export default router; -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Job-pilot 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /client/src/components/themeProvider.jsx: -------------------------------------------------------------------------------- 1 | import { useSelector } from 'react-redux'; 2 | 3 | export default function ThemeProvider({ children }) { 4 | const { theme } = useSelector((state) => state.theme); 5 | return ( 6 |
7 |
8 | {children} 9 |
10 |
11 | ); 12 | } -------------------------------------------------------------------------------- /api/routes/auth.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { getUser, getUsers, signin, signout, signup } from '../controllers/auth.controller.js'; 3 | 4 | const router = express.Router(); 5 | 6 | router.post('/signup', signup); 7 | router.post('/signin',signin); 8 | 9 | router.post('/signout',signout); 10 | router.get('/get-users',getUsers); 11 | router.get('/get-user/:userId',getUser) 12 | 13 | export default router; 14 | -------------------------------------------------------------------------------- /client/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const flowbite = require("flowbite-react/tailwind"); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | export default { 5 | content: [ 6 | "./index.html", 7 | "./src/**/*.{js,ts,jsx,tsx}", 8 | flowbite.content(), 9 | ], 10 | theme: { 11 | extend: {}, 12 | }, 13 | plugins: [ 14 | flowbite.plugin(), 15 | require('tailwind-scrollbar'), 16 | 17 | 18 | ], 19 | } -------------------------------------------------------------------------------- /api/controllers/contact.controller.js: -------------------------------------------------------------------------------- 1 | import Contact from "../models/contact.model.js"; 2 | 3 | export const addContact = async (req, res, next) => { 4 | try { 5 | const {name , email, message}= req.body; 6 | 7 | const newContact = new Contact({ 8 | name, 9 | email, 10 | message 11 | }); 12 | await newContact.save(); 13 | res.status(200).json(newContact); 14 | 15 | 16 | } catch (error) { 17 | next(error); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /api/models/contact.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const contactSchema = new mongoose.Schema({ 4 | name: { 5 | type: String, 6 | required: true, 7 | }, 8 | email: { 9 | type: String, 10 | required: true, 11 | }, 12 | message: { 13 | type: String, 14 | required: true, 15 | }, 16 | }, 17 | { timestamps: true } 18 | ); 19 | 20 | const Contact = mongoose.model('Contact', contactSchema); 21 | export default Contact; -------------------------------------------------------------------------------- /api/utils/verifyUser.js: -------------------------------------------------------------------------------- 1 | import jwt from 'jsonwebtoken'; 2 | import { errorHandler } from './error.js'; 3 | 4 | 5 | export const verifyToken = (req, res, next) => { 6 | const token = req.cookies.access_token; 7 | if (!token) { 8 | return next(errorHandler(401, 'Unauthorized')); 9 | } 10 | jwt.verify(token, process.env.JWT_SECRET, (err, user) => { 11 | if (err) { 12 | return next(errorHandler(401, 'Unauthorized')); 13 | } 14 | req.user = user; 15 | next(); 16 | }); 17 | }; -------------------------------------------------------------------------------- /client/src/redux/theme/themeSlice.js: -------------------------------------------------------------------------------- 1 | import {createSlice} from '@reduxjs/toolkit'; 2 | 3 | const initialState = { 4 | theme: 'light', 5 | }; 6 | 7 | const themeSlice = createSlice({ 8 | name: 'theme', 9 | initialState, 10 | reducers: { 11 | toggleTheme: (state) => { 12 | state.theme = state.theme === 'light' ? 'dark' : 'light'; 13 | }, 14 | } 15 | }); 16 | 17 | export const {toggleTheme} = themeSlice.actions; 18 | 19 | export default themeSlice.reducer; -------------------------------------------------------------------------------- /client/src/components/Poster.Postjob.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import SettingNavBar from './CreatePostNavBar' 3 | import { Link, Outlet, useLocation } from 'react-router-dom'; 4 | 5 | 6 | export default function PosterPostjob() { 7 | 8 | return ( 9 |
10 |
11 | 12 |
13 | 14 | 15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | server: { 7 | proxy: { 8 | '/api': { 9 | target: 'http://localhost:4500', // Backend API URL 10 | secure: false, // Disable SSL verification for localhost 11 | changeOrigin: true, // Adjusts the origin of the request to the target URL 12 | }, 13 | }, 14 | }, 15 | 16 | plugins: [react()], 17 | }) 18 | -------------------------------------------------------------------------------- /api/routes/jobseeker.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { deleteSeeker, getJobSeekerByID, getJobSeekers, updateSeeker } from '../controllers/jobseeker.controller.js'; 4 | import { verifyToken } from '../utils/verifyUser.js'; 5 | 6 | const router = express.Router(); 7 | 8 | router.get('/get',getJobSeekers); //retrive jobSeekers 9 | router.get('/get/:userId',getJobSeekerByID) 10 | router.put('/update/:userId',verifyToken,updateSeeker); 11 | router.delete('/delete/:userId',verifyToken,deleteSeeker); 12 | 13 | 14 | 15 | export default router; -------------------------------------------------------------------------------- /api/routes/jobposter.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import { verifyToken } from '../utils/verifyUser.js'; 4 | import { deleteJobposter, getJobPosterByID, getJobPosters, updateJobposter } from '../controllers/jobposter.controller.js'; 5 | 6 | const router = express.Router(); 7 | 8 | router.get('/get',getJobPosters); //retrive jobPoster 9 | router.get('/get/:userId',verifyToken,getJobPosterByID) 10 | router.put('/update/:userId',verifyToken,updateJobposter); 11 | router.delete('/delete/:userId',verifyToken,deleteJobposter); 12 | 13 | 14 | 15 | export default router; -------------------------------------------------------------------------------- /api/routes/message.route.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import { verifyToken } from '../utils/verifyUser.js'; 3 | import { createMessage, getMessage, getPosterMessage, getReciveMessage } from "../controllers/messaage.controller.js"; 4 | 5 | const router = express.Router() 6 | 7 | router.post('/create-message/:senderId/:reciverId',verifyToken,createMessage); 8 | router.get('/get-message/:senderId/:reciveId/:postId',verifyToken,getMessage); 9 | router.get('/get-chat/:reciveId',verifyToken,getReciveMessage); 10 | router.get('/get-poster-message/:sendId/:postId',verifyToken,getPosterMessage); 11 | 12 | export default router -------------------------------------------------------------------------------- /api/routes/comment.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import {verifyToken} from '../utils/verifyUser.js' 3 | import { createComment, deleteComment, editComment, getPostComments, likeComment } from '../controllers/comment.controller.js'; 4 | 5 | 6 | const router = express.Router(); 7 | router.post('/create',verifyToken, createComment); 8 | router.get('/getPostComments/:postId',getPostComments); 9 | router.put('/likeComment/:commentId', verifyToken,likeComment) 10 | router.put('/editComment/:commentId', verifyToken,editComment) 11 | router.delete('/deleteComment/:commentId', verifyToken,deleteComment) 12 | 13 | export default router; -------------------------------------------------------------------------------- /client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | import {store, persistor} from './redux/store.js' 6 | import {Provider} from 'react-redux' 7 | import { PersistGate } from 'redux-persist/integration/react' 8 | import ThemeProvider from './components/themeProvider.jsx' 9 | 10 | ReactDOM.createRoot(document.getElementById('root')).render( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ); 20 | -------------------------------------------------------------------------------- /api/models/comment.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const commentSchema = new mongoose.Schema({ 4 | content: { 5 | type: String, 6 | required: true, 7 | }, 8 | postId: { 9 | type: String, 10 | required: true, 11 | }, 12 | userId: { 13 | type: String, 14 | required: true 15 | }, 16 | likes: { 17 | type: Array, 18 | default: [], 19 | }, 20 | numberOfLikes: { 21 | type: Number, 22 | default: 0, 23 | }, 24 | }, 25 | {timestamps: true} 26 | ); 27 | const Comment = mongoose.model('Comment',commentSchema); 28 | export default Comment; -------------------------------------------------------------------------------- /api/routes/seeker.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import { addappliedjobs, addcart, deleteCartpost, getapplied, getcart, getJobs } from '../controllers/seeker.controller.js' 3 | 4 | const router = express.Router(); 5 | router.get('/getjobs', getJobs); 6 | router.get('/getjobs/:postId',getJobs) 7 | router.post('/addcart/:userId', addcart); 8 | router.post('/addapplied/:userId', addappliedjobs); 9 | router.get('/getcart/:userId', getcart); 10 | router.get('/getapplied/:userId', getapplied); 11 | router.delete('/deletecartpost/:cartPostIdToDelete/:currentUserId',deleteCartpost) 12 | 13 | //router.get('/getcart/:userId', getcart); 14 | export default router; -------------------------------------------------------------------------------- /client/src/pages/Poster.Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { Outlet, useLocation } from "react-router-dom"; 3 | import PosterDashSidebar from '../components/Poster.DashSidebar'; 4 | import PosterDashOverview from '../components/Poster.DashOverview'; 5 | import PosterSetting from "../components/poster.Setting"; 6 | 7 | export default function PosterDashboard() { 8 | 9 | return ( 10 |
11 |
12 | {/*sidebar*/} 13 | 14 |
15 | 16 |
17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /client/src/firebase.js: -------------------------------------------------------------------------------- 1 | // Import the functions you need from the SDKs you need 2 | import { initializeApp } from "firebase/app"; 3 | // TODO: Add SDKs for Firebase products that you want to use 4 | // https://firebase.google.com/docs/web/setup#available-libraries 5 | 6 | // Your web app's Firebase configuration 7 | const firebaseConfig = { 8 | apiKey: import.meta.env.VITE_FIREBASE_API_KEY, 9 | authDomain: "job-pilot-8ed26.firebaseapp.com", 10 | projectId: "job-pilot-8ed26", 11 | storageBucket: "job-pilot-8ed26.appspot.com", 12 | messagingSenderId: "278079822581", 13 | appId: "1:278079822581:web:a9f08f7671b05b5c71c3bc" 14 | }; 15 | 16 | // Initialize Firebase 17 | export const app = initializeApp(firebaseConfig); -------------------------------------------------------------------------------- /api/routes/post.route.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import { createPost, deletePost, getPostById, getpostForUser, getPosts } from '../controllers/post.controller.js'; 3 | import {updatePost} from '../controllers/post.controller.js' 4 | import { verifyToken } from '../utils/verifyUser.js'; 5 | 6 | const router = express.Router(); 7 | 8 | router.post('/create-post',verifyToken,createPost); 9 | router.put('/update-post/:postId/:userId',verifyToken,updatePost); 10 | router.delete('/delete-post/:postId/:userId',verifyToken,deletePost); 11 | router.get('/get-posts',getPosts); 12 | router.get('/get-post/:userId',verifyToken,getpostForUser); 13 | router.get('/get-job/:postId',verifyToken,getPostById) 14 | 15 | 16 | export default router; 17 | 18 | -------------------------------------------------------------------------------- /api/models/message.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const messageShema = new mongoose.Schema( 4 | 5 | { 6 | reciveId:{ 7 | type:String, 8 | require:true 9 | }, 10 | 11 | sendId:{ 12 | type:String, 13 | require:true 14 | }, 15 | 16 | postId:{ 17 | type:String, 18 | require:true 19 | }, 20 | 21 | message:{ 22 | type:String, 23 | }, 24 | file:{ 25 | type:String, 26 | }, 27 | image:{ 28 | type:String 29 | } 30 | },{timestamps:true} 31 | ); 32 | 33 | const Message = mongoose.model('Message',messageShema); 34 | export default Message; -------------------------------------------------------------------------------- /client/src/redux/store.js: -------------------------------------------------------------------------------- 1 | import { configureStore, combineReducers } from '@reduxjs/toolkit'; 2 | import userReducer from './user/userSlice'; 3 | import themeReducer from './theme/themeSlice' 4 | import { persistReducer, persistStore } from 'redux-persist'; 5 | import storage from 'redux-persist/lib/storage'; 6 | 7 | 8 | const rootReducer = combineReducers({ 9 | user: userReducer, 10 | theme: themeReducer, 11 | 12 | }); 13 | 14 | const persistConfig = { 15 | key: 'root', 16 | storage, 17 | version: 1, 18 | }; 19 | 20 | const persistedReducer = persistReducer(persistConfig, rootReducer); 21 | 22 | export const store = configureStore({ 23 | reducer: persistedReducer, 24 | middleware: (getDefaultMiddleware) => 25 | getDefaultMiddleware({ serializableCheck: false }), 26 | }); 27 | 28 | export const persistor = persistStore(store); -------------------------------------------------------------------------------- /client/src/components/poster.Setting.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import SettingNavBar from './CreatePostNavBar' 3 | import { useLocation } from 'react-router-dom'; 4 | 5 | 6 | 7 | export default function PosterSetting() { 8 | 9 | const location = useLocation(); 10 | //const { currentUser } = useSelector((state) => state.user); 11 | const [tabs, setTabs] = useState(""); 12 | 13 | useEffect(() => { 14 | const urlParams = new URLSearchParams(location.search); 15 | const tabFromUrl = urlParams.get("tabs"); 16 | if (tabFromUrl) { 17 | setTabs(tabFromUrl); 18 | } 19 | }, [location.search]); 20 | return ( 21 | 22 |
23 |
24 | 25 |
26 | 27 | 28 |
29 | ) 30 | } 31 | 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "job-pilot", 3 | "version": "1.0.0", 4 | "description": "RAD project using MERN and tailwind css", 5 | "type": "module", 6 | "main": "index.js", 7 | "scripts": { 8 | "dev": "nodemon api/index.js", 9 | "start": "node api/index.js" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "bcryptjs": "^2.4.3", 16 | "cookie-parser": "^1.4.6", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.5", 19 | "express": "^4.21.1", 20 | "express-async-handler": "^1.2.0", 21 | "job-pilot": "file:", 22 | "jsonwebtoken": "^9.0.2", 23 | "mongoose": "^8.4.4", 24 | "nodemailer": "^6.9.14", 25 | "nodemon": "^3.1.4", 26 | "react": "^18.3.1", 27 | "react-redux": "^9.1.2", 28 | "redux": "^5.0.1", 29 | "socket.io": "^4.7.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/src/components/Poster.Dashusers.jsx: -------------------------------------------------------------------------------- 1 | import { Table } from 'flowbite-react' 2 | import React from 'react' 3 | 4 | export default function PosterDashuser() { 5 | return ( 6 |
7 |
8 |

Jobs Requested List

9 | 10 | 11 | Post Title 12 | Finder Name 13 | Post Status 14 | Post Type 15 | Your Response 16 | 17 | 18 | 19 | 20 |
21 |
22 |
23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Sachith Abeywardhana 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /api/models/savecandidate.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const responseScheme = new mongoose.Schema({ 4 | posterId:{ 5 | type:String, 6 | required: true, 7 | 8 | }, 9 | seekerId:{ 10 | type:String, 11 | required: true, 12 | }, 13 | postId:{ 14 | type:String, 15 | required:true, 16 | }, 17 | response:{ 18 | type:String, 19 | required:false, 20 | 21 | }, 22 | name:{ 23 | type:String, 24 | required:true, 25 | }, 26 | address:{ 27 | type:String, 28 | required:false, 29 | 30 | }, 31 | age:{ 32 | type:String, 33 | required:false, 34 | 35 | }, 36 | nic:{ 37 | type:String, 38 | required:false, 39 | 40 | }, 41 | email:{ 42 | type:String, 43 | required:false, 44 | 45 | }, 46 | 47 | 48 | 49 | 50 | }, 51 | { timestamps: true } 52 | ); 53 | const resp = mongoose.model("resp", responseScheme); 54 | export default resp; -------------------------------------------------------------------------------- /api/controllers/email.controller.js: -------------------------------------------------------------------------------- 1 | import expressAsyncHandler from 'express-async-handler'; 2 | import dotenv from 'dotenv'; 3 | import nodemailer from 'nodemailer'; 4 | 5 | 6 | dotenv.config(); 7 | 8 | const transporter = nodemailer.createTransport({ 9 | host: process.env.SMTP_HOST, 10 | port: process.env.SMTP_PORT, 11 | secure: false, // Use `true` for port 465, `false` for all other ports 12 | auth: { 13 | user: process.env.SMTP_MAIL, 14 | pass: process.env.SMTP_PASSWORD, 15 | }, 16 | }); 17 | 18 | 19 | const sendEmail = expressAsyncHandler( async (req,res)=>{ 20 | const {name,email,subject, message} =req.body; 21 | console.log(name,email,message); 22 | 23 | var mailOptions ={ 24 | from: process.env.SMTP_MAIL, 25 | to:email, 26 | subject:subject, 27 | message:message, 28 | }; 29 | 30 | transporter.sendMail(mailOptions, function(error,info){ 31 | if (error) { 32 | console.log(error); 33 | res.status(500).json({ error: "Failed to send email" }); 34 | } else { 35 | console.log("Email sent successfully"); 36 | res.status(200).json({ message: "Email sent successfully" }); 37 | } 38 | 39 | }); 40 | }) 41 | 42 | export default sendEmail; -------------------------------------------------------------------------------- /client/src/pages/Finish.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { HiCheckCircle } from "react-icons/hi"; 3 | import { FaArrowRight } from 'react-icons/fa'; 4 | import { Button } from 'flowbite-react'; 5 | 6 | export default function Finish() { 7 | return ( 8 |
9 |
10 | 11 |

🎉 Congratulations, Your profile is 100% complete!

12 |

13 | Start exploring your personalized dashboard. Update your profile information to get the most out of our services. Begin using our services and discover how we can assist you. 14 |

15 |
16 | 19 | 25 | 26 |
27 |
28 |
29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /client/src/components/CreatePostNavBar.jsx: -------------------------------------------------------------------------------- 1 | import { Navbar } from 'flowbite-react' 2 | import React, { useEffect, useState } from 'react' 3 | import { Link, useLocation, useParams, useSearchParams } from 'react-router-dom' 4 | 5 | export default function SettingNavBar() { 6 | //const [searchParams,SetSearchparam]= useSearchParams(); 7 | //const tabsFromUrl = searchParams.get('tabs') ==='company'; 8 | 9 | const location = useLocation(); 10 | //const { currentUser } = useSelector((state) => state.user); 11 | const [tab, setTab] = useState(""); 12 | useEffect(() => { 13 | const urlParams = new URLSearchParams(location.search); 14 | const tabFromUrl = urlParams.get("tab"); 15 | if (tabFromUrl) { 16 | setTab(tabFromUrl); 17 | } 18 | }, [location.search]); 19 | return ( 20 |
21 | 22 | 23 | 24 | 25 | 26 | 👥 Company Info 27 | 28 | 29 | 👤 Founding Info 30 | 31 | 32 | 🌐 Social Media Profile 33 | 34 | 35 | ⚙️ Account Setting 36 | 37 | 38 | 39 | 40 | 41 |
42 | 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /client/src/components/Seeker.DashCart.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useSelector } from 'react-redux'; 3 | import SeekerCartPost from './Seeker.cartPost'; 4 | 5 | export default function SeekerDashCart() { 6 | const { currentUser } = useSelector((state) => state.user); 7 | const [userCart, setUserCart] = useState([]); 8 | 9 | useEffect(() => { 10 | const fetchcart = async () => { 11 | try { 12 | const res = await fetch(`api/seeker/getcart/${currentUser._id}`); 13 | 14 | const data1 = await res.json(); 15 | const data = data1.cartPosts; 16 | console.log(data); 17 | if (res.ok) { 18 | setUserCart(data); 19 | console.log(data) 20 | 21 | } 22 | } catch (error) { 23 | console.log(error.message); 24 | } 25 | }; 26 | 27 | fetchcart(); 28 | 29 | }, [currentUser._id]); 30 | 31 | return ( 32 |
33 |
34 |
35 | {userCart.length > 0 && ( 36 | userCart.map((post) => ( 37 | 43 | )))} 44 |
45 |
46 |
47 | ); 48 | } 49 | -------------------------------------------------------------------------------- /api/models/post.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | 4 | const postSchema = new mongoose.Schema( 5 | { 6 | userId:{ 7 | type:String, 8 | required:true 9 | }, 10 | title:{ 11 | required:true, 12 | type:String, 13 | }, 14 | 15 | type:{ 16 | type:String 17 | }, 18 | essential:{ 19 | type:String 20 | }, 21 | requirement:{ 22 | type:String 23 | }, 24 | companyLink:{ 25 | type:String 26 | }, 27 | companyEmail:{ 28 | type:String 29 | }, 30 | venue:{ 31 | type:String 32 | }, 33 | date:{ 34 | type:Date 35 | }, 36 | sTime :{ 37 | type:String 38 | }, 39 | eTime:{ 40 | type:String 41 | }, 42 | salary:{ 43 | type:String 44 | }, 45 | members:{ 46 | type:Number 47 | }, 48 | gender:{ 49 | type:String 50 | }, 51 | description:{ 52 | type:String, 53 | }, 54 | companyName:{ 55 | type:String, 56 | 57 | }, 58 | image:{ 59 | type:String, 60 | default:'https://sebringohio.net/wp-content/uploads/2021/03/job_posting.jpg' 61 | }, 62 | number:{ 63 | type:String, 64 | } 65 | 66 | },{timestamps:true} 67 | ); 68 | 69 | const Post = mongoose.model('Post',postSchema); 70 | export default Post -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emailjs/browser": "^4.4.1", 14 | "@react-pdf-viewer/core": "^3.12.0", 15 | "@react-pdf-viewer/default-layout": "^3.12.0", 16 | "@reduxjs/toolkit": "^2.2.7", 17 | "axios": "^1.7.5", 18 | "client": "file:", 19 | "firebase": "^10.12.4", 20 | "flowbite": "^2.3.0", 21 | "flowbite-react": "^0.10.1", 22 | "moment": "^2.30.1", 23 | "react": "^18.3.1", 24 | "react-circular-progressbar": "^2.1.0", 25 | "react-dom": "^18.3.1", 26 | 27 | "react-icon": "^1.0.0", 28 | "react-icons": "^5.3.0", 29 | "react-pdf": "^9.1.1", 30 | "react-quill": "^2.0.0", 31 | "react-redux": "^9.1.2", 32 | "react-router-dom": "^6.24.1", 33 | "redux-persist": "^6.0.0", 34 | "socket.io-client": "^4.7.5" 35 | 36 | }, 37 | "devDependencies": { 38 | "@types/react": "^18.3.3", 39 | "@types/react-dom": "^18.3.0", 40 | "@vitejs/plugin-react-swc": "^3.7.0", 41 | "autoprefixer": "^10.4.19", 42 | "eslint": "^8.57.0", 43 | "eslint-plugin-react": "^7.34.2", 44 | "eslint-plugin-react-hooks": "^4.6.2", 45 | "eslint-plugin-react-refresh": "^0.4.7", 46 | "postcss": "^8.4.38", 47 | "tailwind-scrollbar": "^3.1.0", 48 | "tailwindcss": "^3.4.4", 49 | 50 | "vite": "^5.4.8" 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /client/src/pages/NotFound.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const NoFound = () => { 4 | return ( 5 |
6 | {/* Container */} 7 |
8 | {/* Image or Illustration */} 9 |
10 | No results illustration 15 |
16 |

404 Error

17 | {/* Title */} 18 |

No results found

19 | 20 | {/* Description */} 21 |

22 | We couldn't find what you searched for. Try searching again. 23 |

24 | 25 | {/* Search Again Button */} 26 | 30 | Go Back To Home 31 | 32 |
33 |
34 | ); 35 | }; 36 | 37 | export default NoFound; 38 | -------------------------------------------------------------------------------- /client/src/pages/Seeker.Dashboard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | import SeekerDashSidebar from "../components/Seeker.DashSidebar"; 4 | import SeekerDashOverview from "../components/Seeker.DashOverview"; 5 | import SeekerDashAppliedjobs from "../components/Seeker.DashAppliedjobs"; 6 | import SeekerDashLatestjobs from "../components/Seeker.DashLatestjobs"; 7 | import SeekerDashCart from "../components/Seeker.DashCart"; 8 | import SeekerProfile from "../components/Seeker.Profile"; 9 | 10 | export default function SeekerDashboard() { 11 | const location = useLocation(); 12 | const [tab, setTab] = useState(""); 13 | useEffect(() => { 14 | const urlParams = new URLSearchParams(location.search); 15 | const tabFromUrl = urlParams.get("tab"); 16 | if (tabFromUrl) { 17 | setTab(tabFromUrl); 18 | } 19 | }, [location.search]); 20 | return ( 21 |
22 |
23 | {/*sidebar*/} 24 | 25 | 26 |
27 |
28 | {/*dashboard comp*/} 29 | {tab === 'dash' && } 30 | {/*applied jobs*/} 31 | {tab === 'appliedjobs' && } 32 | {/*latest jobs*/} 33 | {tab === 'latestjobs' && } 34 | {/*cart*/} 35 | {tab === 'cart' && } 36 | 37 | {/*profile*/} 38 | {tab === 'profile' && } 39 |
40 |
41 | 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Introducing JobListings – Your Ultimate Job Connection Platform! 2 | 3 | Finding the perfect job or candidate has never been easier! JobListings is a modern platform that connects job seekers and employers seamlessly. Whether you’re looking to hire top talent or find your dream job, JobListings has got you covered. 4 | 🌟Why JobListings Stands Out: 5 | - Effortless Job Search: A user-friendly interface allows job seekers to explore opportunities with ease. 6 | - Direct CV Submission: Apply for jobs in just a few clicks by submitting your CV directly through the platform. 7 | - Chatting Feature: Job seekers and employers can communicate directly within the platform, making it easy to stay connected throughout the hiring process. 8 | - Tailored Job Posts: Employers can post both full-time and part-time positions, catering to various recruitment needs. 9 | - Seamless Profile Management: Keep profiles up to date effortlessly, whether you’re a job seeker or employer. 10 | -Smooth & Fast Performance: JobListings uses advanced technology for fast loading and smooth operation, even on slower networks. 11 | 12 | ✨Built with Modern Technology: 13 | - Frontend: React, enhanced with Tailwind CSS for sleek, responsive designs. 14 | - Backend: Node.js with Express, using the MVC architecture for clean and efficient code. 15 | - Database: MongoDB for flexible, scalable data management. 16 | - State Management: React Redux for seamless data flow across the app. 17 | 18 | 🔥 Why You’ll Love JobListings: 19 | - Tailored Experience: Discover local or global opportunities based on your preferences. 20 | - Streamlined Hiring: Companies can easily post jobs and manage candidates. 21 | - All-in-One Solution: From job searching to direct applications and messaging, everything is integrated into one platform. 22 | -------------------------------------------------------------------------------- /client/src/components/PostCards.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from 'flowbite-react'; 3 | import { MdDateRange } from 'react-icons/md'; 4 | import { useState, useEffect } from 'react'; 5 | 6 | export default function PostCards({ post }) { 7 | const date = new Date(post.createdAt); 8 | 9 | const time = date.toLocaleTimeString([], { 10 | hour: '2-digit', 11 | minute: '2-digit', 12 | hour12: true, // Use 'false' for 24-hour format 13 | }); 14 | 15 | const formatDate = (dateString) => { 16 | const options = { 17 | year: 'numeric', 18 | month: 'long', 19 | day: 'numeric', 20 | }; 21 | return new Intl.DateTimeFormat('en-US', options).format( 22 | new Date(dateString) 23 | ); 24 | }; 25 | 26 | return ( 27 |
31 |
32 | post-image 33 |
34 |
35 |

{post.title}

36 |

{post.description}

37 |

{post.companyName}

38 |

{post.essential}

39 |

{post.selectType}

40 |
41 |
42 |

{time}

43 |

44 | {formatDate(post.createdAt)} 45 |

46 |
47 |
48 | 56 | 57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /client/src/redux/user/userSlice.js: -------------------------------------------------------------------------------- 1 | import { createSlice } from "@reduxjs/toolkit"; 2 | 3 | const initialState ={ 4 | currentUser: null, 5 | error: null, 6 | loading: false 7 | } 8 | 9 | const userSlice = createSlice({ 10 | name: 'user', 11 | initialState, 12 | reducers: { 13 | signInStart: (state) => { 14 | state.loading = true; 15 | state.error = null; 16 | }, 17 | signInSuccess: (state, action) => { 18 | state.currentUser = action.payload; 19 | state.loading = false; 20 | state.error = null; 21 | }, 22 | signInFailure: (state, action) => { 23 | state.loading = false; 24 | state.error = action.payload; 25 | }, 26 | updateStart: (state) => { 27 | state.loading = true; 28 | state.error = null; 29 | }, 30 | updateSuccess: (state, action) => { 31 | state.currentUser = action.payload; 32 | state.loading = false; 33 | state.error = null; 34 | }, 35 | updateFailure: (state, action) => { 36 | state.loading = false; 37 | state.error = action.payload; 38 | }, 39 | deleteUserStart: (state) => { 40 | state.loading = true; 41 | state.error = null; 42 | }, 43 | deleteUserSuccess: (state) => { 44 | state.currentUser = null; 45 | state.loading = false; 46 | state.error = null; 47 | }, 48 | deleteUserFailure: (state, action) => { 49 | state.loading = false; 50 | state.error = action.payload; 51 | }, 52 | signoutSuccess: (state) => { 53 | state.currentUser = null; 54 | state.error = null; 55 | state.loading = false; 56 | }, 57 | }, 58 | 59 | }); 60 | 61 | export const {signInStart, signInSuccess, signInFailure, updateStart, updateSuccess, updateFailure, deleteUserStart, deleteUserSuccess, deleteUserFailure, signoutSuccess} = userSlice.actions; 62 | 63 | export default userSlice.reducer; -------------------------------------------------------------------------------- /api/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import mongoose from 'mongoose'; 3 | import dotenv from 'dotenv'; 4 | import authRoutes from './routes/auth.route.js'; 5 | import seekerRoutes from './routes/seeker.route.js' 6 | import cookieParser from 'cookie-parser'; 7 | import postRoutes from './routes/post.route.js' 8 | import contactRoutes from './routes/contact.route.js' 9 | import commentRoutes from './routes/comment.route.js' 10 | import jobseekerRoutes from './routes/jobseeker.route.js' 11 | import jobposterRoutes from './routes/jobposter.route.js' 12 | import responseRoutes from './routes/savecandidate.js' 13 | import messageRoutes from './routes/message.route.js' 14 | import cors from 'cors' 15 | import Message from './models/message.model.js'; 16 | 17 | dotenv.config(); 18 | 19 | mongoose.connect("mongodb+srv://ilakshitha7921:ilakshitha7921@cluster0.gfhczos.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0") 20 | 21 | 22 | .then(() => { 23 | console.log('MongoDB is conected'); 24 | // Message.deleteMany({}) 25 | // .then(() => console.log('Messages deleted')) 26 | // .catch(err => console.log('Error deleting messages:', err)); 27 | }) 28 | .catch((err) => { 29 | console.log(err); 30 | 31 | }); 32 | 33 | 34 | const app = express(); 35 | 36 | app.use(express.json()); 37 | app.use(cookieParser()); 38 | app.use(cors({ 39 | 40 | })); 41 | app.listen(4500, () => { 42 | console.log('Server is running port 4500'); 43 | }); 44 | 45 | app.use('/api/auth', authRoutes); 46 | app.use('/api/jobposter', jobposterRoutes); 47 | app.use('/api/seeker', seekerRoutes); 48 | app.use('/api/response',responseRoutes); 49 | 50 | 51 | 52 | 53 | app.use('/api/post',postRoutes); 54 | app.use('/api/jobseeker',jobseekerRoutes); 55 | app.use('/api/comment', commentRoutes) 56 | app.use('/api/post', postRoutes); 57 | app.use('/api/contact', contactRoutes); 58 | app.use('/api/message',messageRoutes) 59 | 60 | 61 | 62 | 63 | app.use((err, req, res, next) => { 64 | const statusCode = err.statusCode || 500; 65 | const message = err.message || 'Internal Server Error'; 66 | res.status(statusCode).json({ 67 | success: false, 68 | statusCode, 69 | message, 70 | }); 71 | 72 | }); -------------------------------------------------------------------------------- /client/src/components/JobPostCard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Label } from 'flowbite-react'; 3 | import { useNavigate } from 'react-router-dom'; 4 | 5 | const JobPostCard = ({ post }) => { 6 | const [isExpanded, setIsExpanded] = useState(false); 7 | const characterLimit = 100; // Set your character limit here 8 | const navigate = useNavigate(); 9 | 10 | const toggleReadMore = () => { 11 | setIsExpanded(!isExpanded); 12 | }; 13 | 14 | const handleNavigate = () => { 15 | navigate(`full-post/${post._id}`) 16 | } 17 | 18 | return ( 19 |
20 | Job post 26 |
27 |

{post.title}

28 | 31 |
32 |
33 |

{post.requirement}

34 |

{new Date(post.updatedAt).toLocaleDateString()}

35 |

{post.companyName}

36 |
37 |
38 |

39 | {isExpanded ? post.description : `${post.description.slice(0, characterLimit)}...`} 40 |

41 | {post.description.length > characterLimit && ( 42 | 48 | )} 49 |
50 |
51 | ); 52 | }; 53 | 54 | export default JobPostCard; 55 | -------------------------------------------------------------------------------- /client/src/components/Seeker.DashSidebar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Sidebar } from 'flowbite-react' 3 | import {HiViewGrid,HiClipboardList, HiBell} from "react-icons/hi" 4 | import {Link, useLocation} from 'react-router-dom' 5 | import { useEffect, useState } from 'react' 6 | import {useSelector} from 'react-redux' 7 | import { FaHeart, FaShoppingCart } from 'react-icons/fa' 8 | import { FaUser } from 'react-icons/fa' 9 | 10 | export default function SeekerDashSidebar() { 11 | const location = useLocation(); 12 | //const { currentUser } = useSelector((state) => state.user); 13 | const [tab, setTab] = useState(""); 14 | useEffect(() => { 15 | const urlParams = new URLSearchParams(location.search); 16 | const tabFromUrl = urlParams.get("tab"); 17 | if (tabFromUrl) { 18 | setTab(tabFromUrl); 19 | } 20 | }, [location.search]); 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | Overview 34 | 35 | 36 | 37 | 38 | 39 | 40 | Applied Jobs 41 | 42 | 43 | 44 | 45 | 46 | 47 | Latest Jobs 48 | 49 | 50 | 51 | 52 | 53 | 54 | Favourite 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | Profile 63 | 64 | 65 | 66 | 67 | 68 | 69 | ) 70 | } 71 | -------------------------------------------------------------------------------- /api/controllers/savecandidate.controller.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | import resp from "../models/savecandidate.js" 3 | 4 | export const applyForJob = async(req,res,next)=>{ 5 | const {posterId,seekerId,postId,response,name,address,age,nic,email} = req.body; 6 | try { 7 | const application = new resp({ 8 | posterId,seekerId,postId,response,name,address,age,nic,email 9 | } 10 | ) 11 | const savedApplication = await application.save(); 12 | res.send(200).json({savedApplication}); 13 | } catch (error) { 14 | next(error) 15 | } 16 | } 17 | 18 | export const allApplications = async (req, res, next) => { 19 | try { 20 | const allApplicants = await resp.find({}); 21 | res.status(200).json({ allApplicants }); 22 | } catch (error) { 23 | next(error); 24 | } 25 | }; 26 | 27 | 28 | export const findApplicant = async(req,res,next)=>{ 29 | const seekerId = req.params; 30 | try{ 31 | const seeker = await resp.find(seekerId); 32 | res.status(200).json({seeker}); 33 | }catch(error){ 34 | next(error) 35 | } 36 | } 37 | 38 | 39 | 40 | 41 | export const createposterresponse = async(req,res,next)=>{ 42 | 43 | try { 44 | const posterresponse = await response.findByIdAndUpdate( 45 | req.params.postId,req.params.userId, 46 | 47 | { 48 | $set:{ 49 | posterresponse:req.body.posterresponse, 50 | } 51 | } 52 | 53 | ) 54 | res.status(200).json(updatePost) 55 | } catch (error) { 56 | next(error) 57 | } 58 | } 59 | 60 | export const getposterresponse = async(req,res,next)=>{ 61 | try { 62 | const posterresponse = await response.findById( 63 | req.params.postId,req.params.userId, 64 | ) 65 | const res1 = await response.find() 66 | 67 | res.status(200).json({ 68 | res1 69 | }) 70 | } catch (error) { 71 | next(error) 72 | } 73 | } 74 | 75 | export const getseekerresponse = async(req,res,next)=>{ 76 | try { 77 | const seekerresponse = await response.findById( 78 | req.params.postId,req.params.userId, 79 | ) 80 | const res2 = await response.find() 81 | 82 | res.status(200).json({ 83 | res2 84 | }) 85 | } catch (error) { 86 | next(error) 87 | } 88 | } -------------------------------------------------------------------------------- /client/src/components/SearchCard.jsx: -------------------------------------------------------------------------------- 1 | import React ,{useState}from 'react' 2 | import { Link } from 'react-router-dom' 3 | import {Label} from 'flowbite-react' 4 | import CompanyDetailsModal from './Seeker.CompanyDetailsModal'; 5 | import SeekerPartTimeDetailsModel from './Seeker.PartTimeDetailsModel'; 6 | 7 | export default function SearchCard({ ShowAddcart, showApply, showDelete, post }) { 8 | 9 | const [isModalOpen, setIsModalOpen] = useState(false); 10 | const [isModalOpenPart, setIsModalOpenPart] = useState(false); 11 | 12 | const handleModalOpen = () => { 13 | setIsModalOpen(true); 14 | }; 15 | 16 | 17 | const handleModalClose = () => { 18 | setIsModalOpen(false); 19 | }; 20 | 21 | const handleModalOpenPart = () => { 22 | setIsModalOpenPart(true); 23 | }; 24 | 25 | const handleModalClosePart = () => { 26 | setIsModalOpenPart(false); 27 | }; 28 | 29 | 30 | return ( 31 | 32 |
33 | Avatar 38 |
39 |

{post.title}

40 |
41 |

{new Date(post.updatedAt).toLocaleDateString()}

42 | 43 |
44 |
45 | 46 | 47 |
48 | 52 | 53 | 54 | 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /api/models/user.model.js: -------------------------------------------------------------------------------- 1 | import mongoose from "mongoose"; 2 | 3 | const userSchema = new mongoose.Schema({ 4 | username: { 5 | type: String, 6 | required: true, 7 | unique: true, 8 | }, 9 | 10 | fullname: { 11 | type: String, 12 | }, 13 | 14 | email: { 15 | type: String, 16 | required: true, 17 | unique: true, 18 | }, 19 | 20 | password: { 21 | type: String, 22 | required: true, 23 | }, 24 | birthday: { 25 | type: String, 26 | default:'undefine', 27 | }, 28 | maritalStatus: { 29 | type: String, 30 | default:'undefine', 31 | }, 32 | gender: { 33 | type: String, 34 | default:'undefine', 35 | }, 36 | 37 | 38 | 39 | role: { 40 | type: String, 41 | enum: ['jobPoster', 'jobSeeker'], 42 | required: true, 43 | }, 44 | 45 | mobileNumber: { 46 | type: String, 47 | }, 48 | 49 | isAdmin: { 50 | type : Boolean, 51 | default: false, 52 | }, 53 | profilePicture: { 54 | type: String, 55 | default:'https://cdn.pixabay.com/photo/2015/10/05/22/37/blank-profile-picture-973460_960_720.png', 56 | }, 57 | 58 | cart: { 59 | type: [String], 60 | }, 61 | 62 | appliedjobs: { 63 | type: [String], 64 | 65 | }, 66 | 67 | 68 | 69 | biography: { 70 | type: String, 71 | default: "I've been passionate about graphic design and digital art from an early age with a keen interest in Website and Mobile Application User Interfaces...", 72 | required: function () { 73 | return this.role === 'jobPoster'; 74 | 75 | } 76 | }, 77 | coverLetter: { 78 | type: String, 79 | default: "Dear ABC?", 80 | required: function () { 81 | return this.role === 'jobPoster'; 82 | } 83 | }, 84 | 85 | //Feild specific to job seeker 86 | cv: { 87 | type: String, 88 | 89 | }, 90 | 91 | skills: { 92 | type: [String], 93 | required: function () { 94 | return this.role === 'jobSeeker'; 95 | } 96 | 97 | 98 | }, 99 | 100 | cart: { 101 | type: [String], 102 | 103 | 104 | }, 105 | appliedjobs: { 106 | type: [String], 107 | 108 | 109 | } 110 | }, 111 | { timestamps: true } 112 | ); 113 | 114 | const User = mongoose.model('User', userSchema); 115 | 116 | export default User; -------------------------------------------------------------------------------- /client/src/pages/ChatList.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { Spinner, TextInput } from 'flowbite-react'; 4 | import { FaSearch } from 'react-icons/fa'; 5 | import { useLocation, useNavigate, useParams } from 'react-router-dom'; 6 | import ChatMessage from '../components/ChatMessage'; 7 | 8 | export default function ChatList() { 9 | 10 | const [loading,setLoading] = useState(false); 11 | const currentUser = useSelector(state => state.user); 12 | const [reciveMessages,setReciveMessages] = useState([]); 13 | 14 | useEffect(() => { 15 | // Function to fetch received messages from the backend 16 | const fetchReceivedMessages = async () => { 17 | try { 18 | const res = await fetch(`/api/message/get-chat/${currentUser.currentUser._id}`) 19 | const data = await res.json(); 20 | if(!res.ok){ 21 | console.log(data.message) 22 | } 23 | if(res.ok){ 24 | setReciveMessages(data.receivedMessages) 25 | console.log(reciveMessages[0]) 26 | } 27 | 28 | } catch (error) { 29 | console.error("Error fetching received messages", error); 30 | } 31 | }; 32 | 33 | fetchReceivedMessages(); 34 | }, []); 35 | 36 | return ( 37 |
38 | {loading && ( 39 |
40 | 41 |
42 | )} 43 |
44 |

45 | Your Chat List 46 |

47 |

Chat With Job finder

48 | 49 | 50 | 51 |
52 | 53 |
54 | { 55 | reciveMessages.length === 0 ? 'NO CHAT YET...' : 56 | reciveMessages.map((message)=>( 57 | 58 | )) 59 | 60 | } 61 |
62 |
63 | 64 |
65 |
66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /client/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | import { Footer } from "flowbite-react"; 2 | import React from 'react' 3 | import { FaFacebook, FaLinkedin, FaInstagram, FaTwitter, FaYoutube } from "react-icons/fa"; 4 | 5 | 6 | 7 | export default function FooterCom() { 8 | return ( 9 |
10 |
11 |
12 |
13 | {/* Company Section */} 14 |
15 |
Company
16 | 24 |
25 | 26 | {/* Updates Section */} 27 |
28 |
Updates
29 | 36 |
37 | 38 | {/* Social Media Icons */} 39 |
40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | {/* Footer Info */} 48 |
49 |

© 2024 Joblisting

50 |

All Rights Reserved

51 | 55 |
56 |
57 |
58 |
59 |
60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /client/src/components/AdminCard.jsx: -------------------------------------------------------------------------------- 1 | import React ,{useState}from 'react' 2 | import { Link } from 'react-router-dom' 3 | import {Label} from 'flowbite-react' 4 | import CompanyDetailsModal from './Seeker.CompanyDetailsModal'; 5 | import SeekerPartTimeDetailsModel from './Seeker.PartTimeDetailsModel'; 6 | 7 | export default function AdminCard({ ShowAddcart, showApply, showDelete, post }) { 8 | //console.log(post) 9 | const [isModalOpen, setIsModalOpen] = useState(false); 10 | const [isModalOpenPart, setIsModalOpenPart] = useState(false); 11 | 12 | const handleModalOpen = () => { 13 | setIsModalOpen(true); 14 | }; 15 | 16 | 17 | const handleModalClose = () => { 18 | setIsModalOpen(false); 19 | }; 20 | 21 | const handleModalOpenPart = () => { 22 | setIsModalOpenPart(true); 23 | }; 24 | 25 | const handleModalClosePart = () => { 26 | setIsModalOpenPart(false); 27 | }; 28 | 29 | return ( 30 | 31 |
32 | { 33 | post.role ? 34 | (Avatar):( 39 | Avatar 44 | ) 45 | } 46 |
47 | { post.role ? (

{post.username}

) : (

{post.title}

)} 48 |
49 | { !post.role && 50 | <> 51 |

{new Date(post.updatedAt).toLocaleDateString()}

52 | 53 | 54 | } 55 | 56 |
57 |
58 | 59 | 60 |
61 | 62 | 63 | 64 | 65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /api/controllers/comment.controller.js: -------------------------------------------------------------------------------- 1 | import Comment from "../models/comment.model.js"; 2 | import { errorHandler } from "../utils/error.js"; 3 | 4 | export const createComment = async (req, res, next) => { 5 | console.log(req.body) 6 | try { 7 | const { content, postId, userId } = req.body; 8 | if(userId !== req.user.id){ 9 | return next(errorHandler(403,'You are not allowed to create this comment')) 10 | } 11 | const newComment = new Comment({ 12 | content, 13 | postId, 14 | userId, 15 | }); 16 | await newComment.save(); 17 | res.status(200).json(newComment) 18 | } catch (error) { 19 | next(error); 20 | } 21 | } 22 | 23 | export const getPostComments = async (req, res, next) =>{ 24 | try { 25 | const comments = await Comment.find({postId: req.params.postId}).sort({ 26 | createdAt: -1, 27 | }) 28 | res.status(200).json(comments) 29 | } catch (error) { 30 | next(error) 31 | } 32 | } 33 | export const likeComment = async (req, res, next) =>{ 34 | try { 35 | const comment = await Comment.findById(req.params.commentId); 36 | if(!comment){ 37 | return next(errorHandler(404,'Comment not found')) 38 | } 39 | const userIndex = comment.likes.indexOf(req.user.id); 40 | if(userIndex === -1){ 41 | comment.numberOfLikes += 1; 42 | comment.likes.push(req.user.id); 43 | }else{ 44 | comment.numberOfLikes -= 1; 45 | comment.likes.splice(userIndex, 1); 46 | } 47 | await comment.save(); 48 | res.status(200).json(comment); 49 | } catch (error) { 50 | next(error) 51 | } 52 | } 53 | 54 | export const editComment = async (req, res, next) => { 55 | try { 56 | const comment = await Comment.findById(req.params.commentId); 57 | if(!comment){ 58 | return next(errorHandler(404, 'Comment not found')) 59 | } 60 | if (comment.userId !== req.user.id && !req.user.isAdmin){ 61 | return next(errorHandler(404, 'You are not allowed to edit this comment')); 62 | 63 | } 64 | const editedCommnet = await Comment.findByIdAndUpdate( 65 | req.params.commentId, 66 | { 67 | content: req.body.content, 68 | }, 69 | { new: true} 70 | ); 71 | res.status(200).json(editedCommnet); 72 | } catch (error) { 73 | next(error) 74 | } 75 | } 76 | 77 | export const deleteComment = async (req, res, next) => { 78 | try { 79 | const comment = await Comment.findById(req.params.commentId); 80 | if(!comment){ 81 | return next(errorHandler(404, 'Comment not found')) 82 | } 83 | if(comment.userId !== req.user.id && !req.user.isAdmin){ 84 | return next(errorHandler(403, 'You are not allowed to delete this comment')) 85 | } 86 | await Comment.findByIdAndDelete(req.params.commentId) 87 | res.status(200).json('Comment has been deleted') 88 | } catch (error) { 89 | next(error) 90 | } 91 | } -------------------------------------------------------------------------------- /client/src/components/ApplicationForm.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Label, TextInput, Textarea, Button } from "flowbite-react"; 3 | 4 | const ApplicationForm = () => { 5 | const [formData, setFormData] = useState({ 6 | posterId: "", 7 | seekerId: "", 8 | postId: "", 9 | response: "", 10 | name: "", 11 | address: "", 12 | age: "", 13 | nic: "", 14 | email: "", 15 | }); 16 | 17 | const handleChange = (e) => { 18 | setFormData({ ...formData, [e.target.name]: e.target.value }); 19 | }; 20 | 21 | const handleSubmit = (e) => { 22 | e.preventDefault(); 23 | console.log(formData); 24 | // Perform form submission or API call 25 | }; 26 | 27 | return ( 28 |
29 | {/* Poster ID */} 30 | 31 | 32 | {/* Seeker ID */} 33 | 34 | 35 | {/* Post ID */} 36 | 37 | 38 | {/* Response */} 39 |
40 |