├── jsconfig.json ├── postcss.config.js ├── public └── assets │ ├── icons │ ├── bank.png │ ├── line.png │ ├── pin.png │ ├── clock.png │ ├── ellipse.png │ ├── mobile.png │ ├── wallet.png │ ├── candidate.png │ ├── horizontal.png │ ├── square.svg │ ├── selector.svg │ ├── user.svg │ ├── calendar.svg │ ├── help.svg │ ├── tick.svg │ ├── users.svg │ ├── document.svg │ ├── mail.svg │ ├── copy.svg │ ├── link.svg │ ├── cog.svg │ ├── briefcase.svg │ ├── menu.svg │ └── loader.svg │ └── images │ ├── cv.png │ ├── guy.jpg │ ├── logo.png │ ├── shake.jpg │ ├── Jacob J.png │ ├── Robert F.png │ ├── Debra Brown.png │ ├── Devon Lane.png │ ├── Jacob Jones.png │ ├── Jane Cooper.png │ ├── Robert Fox.png │ ├── dashboard.png │ ├── Albert Flores.png │ ├── Annette Black.png │ ├── Brooklyn Simm.png │ ├── Jenny Wilson.png │ ├── Savannah Nguy.png │ ├── Cameron William.png │ ├── Leslie Alexander.png │ ├── grid.svg │ └── logo-text.svg ├── components ├── Provider.jsx ├── SideBtn.jsx ├── Category.jsx ├── NavBar.jsx ├── EmailComp.jsx ├── Form.jsx ├── Feed.jsx ├── Inbox.jsx ├── LeftSide.jsx └── RightSideBar.jsx ├── app ├── api │ ├── candidate │ │ ├── route.js │ │ ├── new │ │ │ └── route.js │ │ └── [id] │ │ │ └── route.js │ └── auth │ │ └── [...nextauth] │ │ └── route.js ├── page.jsx ├── layout.jsx ├── create-candidate │ └── page.jsx └── emails │ └── page.jsx ├── next.config.js ├── .gitignore ├── tailwind.config.js ├── package.json ├── utils └── database.js ├── models ├── user.js └── candidate.js ├── README.md └── styles └── globals.css /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/assets/icons/bank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/bank.png -------------------------------------------------------------------------------- /public/assets/icons/line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/line.png -------------------------------------------------------------------------------- /public/assets/icons/pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/pin.png -------------------------------------------------------------------------------- /public/assets/images/cv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/cv.png -------------------------------------------------------------------------------- /public/assets/images/guy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/guy.jpg -------------------------------------------------------------------------------- /public/assets/icons/clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/clock.png -------------------------------------------------------------------------------- /public/assets/icons/ellipse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/ellipse.png -------------------------------------------------------------------------------- /public/assets/icons/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/mobile.png -------------------------------------------------------------------------------- /public/assets/icons/wallet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/wallet.png -------------------------------------------------------------------------------- /public/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/logo.png -------------------------------------------------------------------------------- /public/assets/images/shake.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/shake.jpg -------------------------------------------------------------------------------- /public/assets/icons/candidate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/candidate.png -------------------------------------------------------------------------------- /public/assets/images/Jacob J.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Jacob J.png -------------------------------------------------------------------------------- /public/assets/images/Robert F.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Robert F.png -------------------------------------------------------------------------------- /public/assets/icons/horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/icons/horizontal.png -------------------------------------------------------------------------------- /public/assets/images/Debra Brown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Debra Brown.png -------------------------------------------------------------------------------- /public/assets/images/Devon Lane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Devon Lane.png -------------------------------------------------------------------------------- /public/assets/images/Jacob Jones.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Jacob Jones.png -------------------------------------------------------------------------------- /public/assets/images/Jane Cooper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Jane Cooper.png -------------------------------------------------------------------------------- /public/assets/images/Robert Fox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Robert Fox.png -------------------------------------------------------------------------------- /public/assets/images/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/dashboard.png -------------------------------------------------------------------------------- /public/assets/images/Albert Flores.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Albert Flores.png -------------------------------------------------------------------------------- /public/assets/images/Annette Black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Annette Black.png -------------------------------------------------------------------------------- /public/assets/images/Brooklyn Simm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Brooklyn Simm.png -------------------------------------------------------------------------------- /public/assets/images/Jenny Wilson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Jenny Wilson.png -------------------------------------------------------------------------------- /public/assets/images/Savannah Nguy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Savannah Nguy.png -------------------------------------------------------------------------------- /public/assets/images/Cameron William.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Cameron William.png -------------------------------------------------------------------------------- /public/assets/images/Leslie Alexander.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lodhik9/HR-Buddys_Next-JS_API_System_CSR/HEAD/public/assets/images/Leslie Alexander.png -------------------------------------------------------------------------------- /components/Provider.jsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { SessionProvider } from "next-auth/react"; 4 | 5 | const Provider = ({ children, session }) => ( 6 | 7 | {children} 8 | 9 | ) 10 | 11 | export default Provider; 12 | -------------------------------------------------------------------------------- /public/assets/icons/square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /components/SideBtn.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const SideBtn = ({ title, icon , path}) => { 4 | return ( 5 | 6 |
7 | {icon} 8 |

{title}

9 |
10 | 11 | ); 12 | }; 13 | 14 | export default SideBtn; 15 | -------------------------------------------------------------------------------- /app/api/candidate/route.js: -------------------------------------------------------------------------------- 1 | import Candidate from "@models/candidate"; 2 | import { connectToDB } from "@utils/database"; 3 | 4 | export const GET = async (request) => { 5 | try { 6 | await connectToDB() 7 | 8 | const prompts = await Candidate.find({}).populate('creator') 9 | 10 | return new Response(JSON.stringify(prompts), { status: 200 }) 11 | } catch (error) { 12 | return new Response("Failed to fetch all prompts", { status: 500 }) 13 | } 14 | } -------------------------------------------------------------------------------- /public/assets/icons/selector.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | experimental: { 4 | appDir: true, 5 | serverComponentsExternalPackages: ["mongoose"], 6 | }, 7 | images: { 8 | domains: ['lh3.googleusercontent.com'], 9 | }, 10 | webpack(config) { 11 | config.experiments = { 12 | ...config.experiments, 13 | topLevelAwait: true, 14 | } 15 | return config 16 | } 17 | } 18 | 19 | module.exports = nextConfig 20 | -------------------------------------------------------------------------------- /app/page.jsx: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | 3 | const Home = () => ( 4 |
5 | {/* If we increase the size of the scrolling then we can access the buttons from LeftSide */} 6 | Guy 7 | Guy 8 | 9 |
10 | ); 11 | 12 | export default Home; 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .env 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /components/Category.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Category = ({ logo, title, css }) => { 4 | const modifiedTitle = title === "POSITION" ? title : {title}; 5 | return ( 6 |
7 | 11 |
12 | ); 13 | }; 14 | 15 | export default Category; 16 | -------------------------------------------------------------------------------- /public/assets/icons/user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | './pages/**/*.{js,ts,jsx,tsx,mdx}', 5 | './components/**/*.{js,ts,jsx,tsx,mdx}', 6 | './app/**/*.{js,ts,jsx,tsx,mdx}', 7 | ], 8 | theme: { 9 | extend: { 10 | backgroundImage: { 11 | 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 12 | 'gradient-conic': 13 | 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web-app-final", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "autoprefixer": "10.4.14", 13 | "bcrypt": "^5.1.0", 14 | "mongodb": "^5.6.0", 15 | "mongoose": "^7.2.2", 16 | "next": "13.4.4", 17 | "next-auth": "^4.22.1", 18 | "postcss": "8.4.24", 19 | "react": "18.2.0", 20 | "react-dom": "18.2.0", 21 | "react-icons": "^4.9.0", 22 | "tailwindcss": "3.3.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /utils/database.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose'; 2 | 3 | let isConnected = false; // track the connection 4 | 5 | export const connectToDB = async () => { 6 | mongoose.set('strictQuery', true); 7 | 8 | if(isConnected) { 9 | console.log('MongoDB is already connected'); 10 | return; 11 | } 12 | 13 | try { 14 | await mongoose.connect(process.env.MONGODB_URI, { 15 | dbName: "share_candidate", 16 | useNewUrlParser: true, 17 | useUnifiedTopology: true, 18 | }) 19 | 20 | isConnected = true; 21 | 22 | console.log('MongoDB connected') 23 | } catch (error) { 24 | console.log(error); 25 | } 26 | } -------------------------------------------------------------------------------- /models/user.js: -------------------------------------------------------------------------------- 1 | import { Schema, model, models } from 'mongoose'; 2 | 3 | const UserSchema = new Schema({ 4 | email: { 5 | type: String, 6 | unique: [true, 'Email already exists!'], 7 | required: [true, 'Email is required!'], 8 | }, 9 | username: { 10 | type: String, 11 | required: [true, 'Username is required!'], 12 | // match: [/^(?=.{8,20}$)(?![_.])(?!.*[_.]{2})[a-zA-Z0-9._]+(? 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/assets/icons/help.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/api/candidate/new/route.js: -------------------------------------------------------------------------------- 1 | import Candidate from "@models/Candidate"; 2 | import { connectToDB } from "@utils/database"; 3 | 4 | export const POST = async (request) => { 5 | // console.log(request.json().userName+ "request here"); 6 | const { userId, userName, position,userEmail,experience,location,employment,department,telephone } = await request.json(); 7 | 8 | try { 9 | await connectToDB(); 10 | const newCandidate = new Candidate({ creator: userId, userName, position, userEmail,experience,location,employment,department,telephone }); 11 | 12 | await newCandidate.save(); 13 | return new Response(JSON.stringify(newCandidate), { status: 201 }) 14 | } catch (error) { 15 | return new Response("Failed to create a new Candidate", { status: 500 }); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /public/assets/icons/tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/layout.jsx: -------------------------------------------------------------------------------- 1 | 2 | import "@styles/globals.css"; 3 | 4 | import NavBar from "@components/NavBar"; 5 | import LeftSide from "@components/LeftSide"; 6 | import Inbox from "@components/Inbox"; 7 | import Provider from "@components/Provider"; 8 | 9 | export const metadata = { 10 | title: "hrbuddy", 11 | description: "Discover & Share HR", 12 | }; 13 | 14 | const RootLayout = ({ children }) => ( 15 | 16 | 17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | {children} 28 |
29 |
30 | 31 | 32 | 33 | ); 34 | 35 | export default RootLayout; 36 | -------------------------------------------------------------------------------- /public/assets/icons/users.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/assets/icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/icons/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/assets/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 11 | 14 | -------------------------------------------------------------------------------- /models/candidate.js: -------------------------------------------------------------------------------- 1 | import { Schema, model, models } from 'mongoose'; 2 | 3 | const PromptSchema = new Schema({ 4 | creator: { 5 | type: Schema.Types.ObjectId, 6 | ref: 'User', 7 | }, 8 | userName: { 9 | type: String, 10 | required: [true, 'userName is required.'], 11 | }, 12 | position: { 13 | type: String, 14 | required: [true, 'position is required.'], 15 | }, 16 | 17 | userEmail: { 18 | type: String, 19 | required: [true, 'userEmail is required.'], 20 | }, 21 | experience: { 22 | type: String, 23 | required: [true, 'experience is required.'], 24 | }, 25 | location: { 26 | type: String, 27 | required: [true, 'location is required.'], 28 | }, 29 | employment: { 30 | type: String, 31 | required: [true, 'employment is required.'], 32 | }, 33 | department: { 34 | type: String, 35 | required: [true, 'department is required.'], 36 | }, 37 | telephone: { 38 | type: String, 39 | required: [true, 'telephone is required.'], 40 | } 41 | }); 42 | 43 | const Prompt = models.Prompt || model('Prompt', PromptSchema); 44 | 45 | export default Prompt; -------------------------------------------------------------------------------- /public/assets/icons/link.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 21 | 30 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /public/assets/icons/cog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BiBell } from 'react-icons/bi'; 3 | import { FiSearch } from "react-icons/fi"; 4 | 5 | const Navbar = () => { 6 | return ( 7 |
8 |
9 | 10 | Candidates 11 |
12 | {/* Input */} 13 |
14 |
15 |
16 |
17 | {/* Right Side */} 18 |
19 | {/* {" "} */} 20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 |
28 | ); 29 | }; 30 | 31 | export default Navbar; 32 | -------------------------------------------------------------------------------- /public/assets/icons/briefcase.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/assets/icons/menu.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/assets/images/grid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/api/auth/[...nextauth]/route.js: -------------------------------------------------------------------------------- 1 | import NextAuth from 'next-auth'; 2 | import GoogleProvider from 'next-auth/providers/google'; 3 | 4 | import User from '@models/user'; 5 | import { connectToDB } from '@utils/database'; 6 | 7 | const handler = NextAuth({ 8 | providers: [ 9 | GoogleProvider({ 10 | clientId: process.env.GOOGLE_ID, 11 | clientSecret: process.env.GOOGLE_CLIENT_SECRET, 12 | }) 13 | ], 14 | callbacks: { 15 | async session({ session }) { 16 | // store the user id from MongoDB to session 17 | const options = { maxTimeMS: 2000000 }; // Set timeout to 20 seconds 18 | const sessionUser = await User.findOne({ email: session.user.email }, options); 19 | session.user.id = sessionUser._id.toString(); 20 | 21 | return session; 22 | }, 23 | async signIn({ account, profile, user, credentials }) { 24 | try { 25 | await connectToDB(); 26 | 27 | const options = { maxTimeMS: 2000000 }; // Set timeout to 20 seconds 28 | // check if user already exists 29 | const userExists = await User.findOne({ email: profile.email }, options); 30 | 31 | // if not, create a new document and save user in MongoDB 32 | if (!userExists) { 33 | await User.create({ 34 | email: profile.email, 35 | username: profile.name.replace(" ", "").toLowerCase(), 36 | image: profile.picture, 37 | }); 38 | } 39 | 40 | return true 41 | } catch (error) { 42 | console.log("Error checking if user exists: ", error.message); 43 | return false 44 | } 45 | }, 46 | } 47 | }) 48 | 49 | export { handler as GET, handler as POST } 50 | -------------------------------------------------------------------------------- /app/create-candidate/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import { useSession } from "next-auth/react"; 5 | import { useRouter } from "next/navigation"; 6 | 7 | import Form from "@components/Form"; 8 | 9 | const CreateCandidate = () => { 10 | const router = useRouter(); 11 | const { data: session } = useSession(); 12 | 13 | const [submitting, setIsSubmitting] = useState(false); 14 | const [can, setCan] = useState({ 15 | userName: "", 16 | position: "", 17 | userEmail: "", 18 | experience: "", 19 | location: "", 20 | employment: "", 21 | department: "", 22 | telephone: "" 23 | }); 24 | 25 | // This is CSR data fetching 26 | const createCandidate = async (e) => { 27 | // SSG: By Default the data fetching is SSG:Static Side Generation 28 | // SSR: For dynamic pages use {cache: 'no-store'}. SSR: Server Side Rendering 29 | // ISR: For dynamic data from static sides use {next:{revalidate:10}} ISR: Incremental Static generation 30 | 31 | e.preventDefault(); 32 | setIsSubmitting(true); 33 | 34 | try { 35 | // console.log("candidate api"); 36 | const response = await fetch("/api/candidate/new", { 37 | method: "POST", 38 | body: JSON.stringify({ 39 | userName: can.userName, 40 | userId: session?.user.id, 41 | position: can.position, 42 | userEmail: can.userEmail, 43 | experience: can.experience, 44 | location: can.location, 45 | employment: can.employment, 46 | department: can.department, 47 | telephone: can.telephone, 48 | }), 49 | }); 50 | 51 | if (response.ok) { 52 | router.push("/"); 53 | } 54 | } catch (error) { 55 | console.log(error); 56 | } finally { 57 | setIsSubmitting(false); 58 | } 59 | router.push("/emails"); 60 | }; 61 | 62 | return ( 63 |
70 | ); 71 | }; 72 | 73 | export default CreateCandidate; 74 | -------------------------------------------------------------------------------- /app/api/candidate/[id]/route.js: -------------------------------------------------------------------------------- 1 | import Candidate from "@models/candidate"; 2 | import { connectToDB } from "@utils/database"; 3 | 4 | export const GET = async (request, { params }) => { 5 | try { 6 | await connectToDB(); 7 | 8 | const prompt = await Candidate.findById(params.id).populate("creator"); 9 | if (!prompt) return new Response("Candidate Not Found", { status: 404 }); 10 | 11 | return new Response(JSON.stringify(prompt), { status: 200 }); 12 | } catch (error) { 13 | return new Response("Internal Server Error", { status: 500 }); 14 | } 15 | }; 16 | 17 | export const PATCH = async (request, { params }) => { 18 | const { 19 | userName, 20 | position, 21 | userEmail, 22 | experience, 23 | location, 24 | employment, 25 | department, 26 | telephone, 27 | } = await request.json(); 28 | try { 29 | await connectToDB(); 30 | 31 | // Find the existing prompt by ID 32 | const existingPrompt = await Candidate.findById(params.id); 33 | 34 | if (!existingPrompt) { 35 | return new Response("Candidate not found", { status: 404 }); 36 | } 37 | 38 | // Update the prompt with new data 39 | existingPrompt.userName = userName; 40 | existingPrompt.position = position; 41 | existingPrompt.userEmail = userEmail; 42 | existingPrompt.experience = experience; 43 | existingPrompt.location = location; 44 | existingPrompt.employment = employment; 45 | existingPrompt.department = department; 46 | existingPrompt.telephone = telephone; 47 | 48 | await existingPrompt.save(); 49 | 50 | return new Response("Successfully updated the Prompts", { status: 200 }); 51 | } catch (error) { 52 | return new Response("Error Updating Prompt", { status: 500 }); 53 | } 54 | }; 55 | 56 | export const DELETE = async (request, { params }) => { 57 | try { 58 | await connectToDB(); 59 | 60 | // Find the prompt by ID and remove it 61 | await Candidate.findByIdAndRemove(params.id); 62 | 63 | return new Response("Prompt deleted successfully", { status: 200 }); 64 | } catch (error) { 65 | return new Response("Error deleting prompt", { status: 500 }); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /components/EmailComp.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import Image from "next/image"; 5 | import { useSession } from "next-auth/react"; 6 | import { usePathname, useRouter } from "next/navigation"; 7 | 8 | const EmailComp = ({ 9 | post, 10 | handleEdit, 11 | handleDelete, 12 | handleTagClick, 13 | onClick, 14 | }) => { 15 | const { data: session } = useSession(); 16 | const pathName = usePathname(); 17 | const router = useRouter(); 18 | 19 | const [copied, setCopied] = useState(""); 20 | 21 | const handleProfileClick = () => { 22 | // console.log(post); 23 | 24 | if (post.creator._id === session?.user.id) return router.push("/profile"); 25 | 26 | router.push(`/profile/${post.creator._id}?name=${post.creator.username}`); 27 | }; 28 | 29 | const handleCopy = () => { 30 | setCopied(post.prompt); 31 | navigator.clipboard.writeText(post.prompt); 32 | setTimeout(() => setCopied(false), 3000); 33 | }; 34 | // // Generate a unique key for each email component 35 | const k = `${post.userName}`; 36 | // console.log(key); 37 | return ( 38 |
39 | {post && ( 40 |
44 | Guy 51 | {/* Display the image */} 52 | Guy 59 | 60 |

61 | {" "} 62 | {post.userName} 63 |

64 |
65 |

{post.position}-

66 |

{post.userEmail}

67 |

{post.experience}

68 |

{post.location}

69 |

{post.employment}

70 |
71 |
72 | )} 73 | {/*

here2

*/} 74 |
75 | ); 76 | }; 77 | 78 | export default EmailComp; 79 | -------------------------------------------------------------------------------- /public/assets/icons/loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /components/Form.jsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | const Form = ({ type, post, setPost, submitting, handleSubmit }) => { 4 | return ( 5 |
6 |
7 | 11 | 24 | 25 | 39 | 40 | 54 | 68 | 69 | 83 | 84 | 98 | 99 | 113 | 114 | 128 | 129 |
130 | 131 | Cancel 132 | 133 | 134 | 141 |
142 | 143 |
144 |
145 | ); 146 | }; 147 | 148 | export default Form; 149 | -------------------------------------------------------------------------------- /components/Feed.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState, useEffect } from "react"; 4 | import RightSideBar from "./RightSideBar"; 5 | import EmailComp from "./EmailComp"; 6 | 7 | const CandidateCardList = ({ 8 | data, 9 | handleTagClick, 10 | handleEmailClick, 11 | isMenuOpen, 12 | toggleMenu, 13 | selectedEmail, 14 | selectedEmailImage, 15 | handleNameChange, 16 | handleDepChange, 17 | postID, 18 | name, 19 | dep 20 | }) => { 21 | return ( 22 |
23 |
24 | {data.map((post, index) => ( 25 |
handleEmailClick(post)} 28 | > 29 | 34 |
35 | ))} 36 |
37 | 38 | 49 |
50 | ); 51 | }; 52 | 53 | const Feed = () => { 54 | const [allPosts, setAllPosts] = useState([]); 55 | 56 | // states 57 | const [searchText, setSearchText] = useState(""); 58 | const [searchTimeout, setSearchTimeout] = useState(null); 59 | const [searchedResults, setSearchedResults] = useState([]); 60 | const [isMenuOpen, setIsMenuOpen] = useState(false); 61 | const [selectedEmail, setSelectedEmail] = useState(null); 62 | const [selectedEmailImage, setSelectedEmailImage] = useState(null); // Track the selected email 63 | const [postID, setPostID] = useState(null); 64 | 65 | const [name, setName] = useState(""); 66 | const [dep, setDep] = useState(""); 67 | 68 | const handleNameChange = (e) => { 69 | setName(e.target.value); 70 | }; 71 | const handleDepChange = (e) => { 72 | setDep(e.target.value); 73 | }; 74 | 75 | const fetchPosts = async () => { 76 | // SSG: By Default the data fetching is SSG:Static Side Generation 77 | // SSR: For dynamic pages use {cache: 'no-store'}. SSR: Server Side Rendering 78 | // ISR: For dynamic data from static sides use {next:{revalidate:10}} ISR: Incremental Static generation 79 | 80 | const response = await fetch("/api/candidate"); 81 | const data = await response.json(); 82 | 83 | // console.log(data + "fetched data"); 84 | 85 | setAllPosts(data); 86 | }; 87 | 88 | // This is CSR 89 | useEffect(() => { 90 | fetchPosts(); 91 | }, []); 92 | 93 | const filterPrompts = (searchtext) => { 94 | const regex = new RegExp(searchtext, "i"); // 'i' flag for case-insensitive search 95 | return allPosts.filter( 96 | (item) => 97 | regex.test(item.creator.username) || 98 | regex.test(item.userName) || 99 | regex.test(item.userEmail) 100 | ); 101 | }; 102 | 103 | const handleSearchChange = (e) => { 104 | clearTimeout(searchTimeout); 105 | setSearchText(e.target.value); 106 | 107 | // debounce method 108 | setSearchTimeout( 109 | setTimeout(() => { 110 | const searchResult = filterPrompts(e.target.value); 111 | setSearchedResults(searchResult); 112 | }, 500) 113 | ); 114 | }; 115 | 116 | const handleTagClick = (tagName) => { 117 | setSearchText(tagName); 118 | 119 | const searchResult = filterPrompts(tagName); 120 | setSearchedResults(searchResult); 121 | }; 122 | 123 | const toggleMenu = () => { 124 | setIsMenuOpen(!isMenuOpen); 125 | }; 126 | 127 | const handleEmailClick = (email) => { 128 | setSelectedEmail(email); 129 | setSelectedEmailImage(`/assets/images/${email.userName}.png`); 130 | setIsMenuOpen(true); 131 | setPostID(email._id); 132 | }; 133 | 134 | return ( 135 |
136 | {/* All Prompts */} 137 | {searchText ? ( 138 | 151 | ) : ( 152 | 164 | )} 165 |
166 | ); 167 | }; 168 | 169 | export default Feed; 170 | -------------------------------------------------------------------------------- /components/Inbox.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import Link from "next/link"; 5 | import Image from "next/image"; 6 | import Category from "./Category"; 7 | import { useSession } from "next-auth/react"; 8 | 9 | const Inbox = () => { 10 | const { data: session } = useSession(); 11 | const categories = [ 12 | { 13 | logo: ( 14 | Guy 21 | ), 22 | // logo: , 23 | title: "FULL NAME", 24 | css: "hover:text-yellow-700 focus:text-yellow-500", 25 | }, 26 | { 27 | logo: ( 28 | Guy 35 | ), 36 | 37 | // logo: , 38 | title: "POSITION", 39 | css: "hover:text-purple-700 focus:text-purple-500", 40 | }, 41 | { 42 | logo: ( 43 | Guy 50 | ), 51 | 52 | // logo: , 53 | title: "EMAIL", 54 | css: "hover:text-green-700 focus:text-green-500", 55 | }, 56 | { 57 | logo: ( 58 | Guy 65 | ), 66 | 67 | // logo: , 68 | title: "EXPERIENCE", 69 | css: "hover:text-gray-700 focus:text-gray-500", 70 | }, 71 | { 72 | logo: ( 73 | Guy 80 | ), 81 | 82 | // logo: , 83 | title: "LOCATION", 84 | css: "hover:text-blue-700 focus:text-blue-500", 85 | }, 86 | { 87 | logo: ( 88 | Guy 95 | ), 96 | 97 | // logo: , 98 | title: "EMPLOYMENT", 99 | css: "hover:text-blue-700 focus:text-blue-500", 100 | }, 101 | ]; 102 | return ( 103 |
104 | {/* Edit */} 105 |
106 | {/* Left Side */} 107 |
108 |

109 | New (30) 110 |

111 |

112 | Screening (12) 113 |

114 |

115 | Interview (26) 116 |

117 |

118 | Test (8) 119 |

120 |

121 | Employed (6) 122 |

123 |

124 | Rejected (34) 125 |

126 | 127 |
128 | {/* Right Side */} 129 |
130 |
131 | 134 | 137 | {session?.user ? ( 138 | + Add New 139 | ):( 140 | <> 141 | )} 142 | 143 |
144 |
145 |
146 | {/* Categories */} 147 | 148 |
149 | Guy 156 | 157 | {categories.map((category) => ( 158 | 163 | ))} 164 |
165 | {/* Mails */} 166 |
167 | ); 168 | }; 169 | 170 | export default Inbox; 171 | -------------------------------------------------------------------------------- /app/emails/page.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import Feed from "@components/Feed"; 5 | 6 | const Emails = () => { 7 | const emails = [ 8 | { 9 | userName: "Debra Brown", 10 | position: "Product Owner", 11 | userEmail: "alma@example.com", 12 | experience: "11:00 AM", 13 | location: "Hamburg", 14 | employment: "Full time" 15 | }, 16 | { 17 | userName: "Cameron Williamson", 18 | position: "Product Owner", 19 | userEmail: "kenzi@example.com", 20 | experience: "9:00 AM ", 21 | location: "Berlin", 22 | employment: "Full time" 23 | }, 24 | { 25 | userName: "Brooklyn Simmons", 26 | position: "Sales Manager", 27 | userEmail: "willie@example.com", 28 | experience: "Dec 3", 29 | location: "Hamburg", 30 | employment: "Full time" 31 | }, 32 | { 33 | userName: "Jane Cooper", 34 | position: "Product Owner", 35 | userEmail: "rivera@example.com", 36 | experience: "Dec 2", 37 | location: "Hamburg", 38 | employment: "Full time" 39 | }, 40 | { 41 | userName: "Leslie Alexander", 42 | position: "UI/UX Designer", 43 | userEmail: "bill@example.com", 44 | experience: "11:00 AM", 45 | location: "Hamburg", 46 | employment: "Full time" 47 | }, 48 | { 49 | userName: "Robert Fox", 50 | position: "Product Owner", 51 | userEmail: "deanna@example.com", 52 | experience: "9:00 AM ", 53 | location: "Hamburg", 54 | employment: "Full time" 55 | }, 56 | { 57 | userName: "Jacob Jones", 58 | position: "Backend Developer", 59 | userEmail: "graham@example.com", 60 | experience: "Dec 3", 61 | location: "Hamburg", 62 | employment: "Full time" 63 | }, 64 | { 65 | userName: "Savannah Nguyen", 66 | position: "Sales Manager", 67 | userEmail: "nevaeh@example.com", 68 | experience: "Dec 2", 69 | location: "Hamburg", 70 | employment: "Full time" 71 | }, 72 | { 73 | userName: "Albert Flores", 74 | position: "Sales Manager", 75 | userEmail: "dolores@example.com", 76 | experience: "11:00 AM", 77 | location: "Hamburg", 78 | employment: "Full time" 79 | }, 80 | { 81 | userName: "Annette Black", 82 | position: "Sales Manager", 83 | userEmail: "dolores@example.com", 84 | experience: "9:00 AM ", 85 | location: "Hamburg", 86 | employment: "Full time" 87 | }, 88 | { 89 | userName: "Devon Lane", 90 | position: "Sales Manager", 91 | userEmail: "dolores@example.com", 92 | experience: "Dec 3", 93 | location: "Hamburg", 94 | employment: "Full time" 95 | }, 96 | { 97 | userName: "Annette Black", 98 | position: "Sales Manager", 99 | userEmail: "dolores@example.com", 100 | experience: "Dec 2", 101 | location: "Hamburg", 102 | employment: "Full time" 103 | }, 104 | { 105 | userName: "Annette Black", 106 | position: "Sales Manager", 107 | userEmail: "dolores@example.com", 108 | experience: "11:00 AM", 109 | location: "Hamburg", 110 | employment: "Full time" 111 | }, 112 | { 113 | userName: "Annette Black", 114 | position: "Sales Manager", 115 | userEmail: "dolores@example.com", 116 | experience: "9:00 AM ", 117 | location: "location", 118 | employment: "employment" 119 | }, 120 | { 121 | userName: "Annette Black", 122 | position: "Sales Manager", 123 | userEmail: "dolores@example.com", 124 | experience: "Dec 3", 125 | location: "location", 126 | employment: "Full time" 127 | }, 128 | { 129 | userName: "Annette Black", 130 | position: "Sales Manager", 131 | userEmail: "dolores@example.com", 132 | experience: "Dec 2", 133 | location: "location", 134 | employment: "Full time" 135 | }, 136 | { 137 | userName: "Annette Black", 138 | position: "Sales Manager", 139 | userEmail: "dolores@example.com", 140 | experience: "11:00 AM", 141 | location: "location", 142 | employment: "Full time" 143 | }, 144 | { 145 | userName: "Annette Black", 146 | position: "Sales Manager", 147 | userEmail: "dolores@example.com", 148 | experience: "9:00 AM ", 149 | location: "location", 150 | employment: "Full time" 151 | }, 152 | { 153 | userName: "Annette Black", 154 | position: "Sales Manager", 155 | userEmail: "dolores@example.com", 156 | experience: "Dec 3", 157 | location: "location", 158 | employment: "employment" 159 | }, 160 | { 161 | userName: "Annette Black", 162 | position: "Sales Manager", 163 | userEmail: "dolores@example.com", 164 | experience: "Dec 2", 165 | location: "location", 166 | employment: "employment" 167 | }, 168 | { 169 | userName: "Annette Black", 170 | position: "Sales Manager", 171 | userEmail: "dolores@example.com", 172 | experience: "11:00 AM", 173 | location: "location", 174 | employment: "employment" 175 | }, 176 | { 177 | userName: "Annette Black", 178 | position: "Sales Manager", 179 | userEmail: "dolores@example.com", 180 | experience: "9:00 AM ", 181 | location: "location", 182 | employment: "employment" 183 | }, 184 | { 185 | userName: "Annette Black", 186 | position: "Sales Manager", 187 | userEmail: "dolores@example.com", 188 | experience: "Dec 3", 189 | location: "location", 190 | employment: "employment" 191 | }, 192 | { 193 | userName: "Annette Black", 194 | position: "Sales Manager", 195 | userEmail: "dolores@example.com", 196 | experience: "Dec 2", 197 | location: "location", 198 | employment: "employment" 199 | }, 200 | { 201 | userName: "Annette Black", 202 | position: "Sales Manager", 203 | userEmail: "dolores@example.com", 204 | experience: "Dec 2", 205 | location: "location", 206 | employment: "employment" 207 | }, 208 | { 209 | userName: "Annette Black", 210 | position: "Sales Manager", 211 | userEmail: "dolores@example.com", 212 | experience: "11:00 AM", 213 | location: "location", 214 | employment: "employment" 215 | }, 216 | { 217 | userName: "Annette Black", 218 | position: "Sales Manager", 219 | userEmail: "dolores@example.com", 220 | experience: "9:00 AM ", 221 | location: "location", 222 | employment: "employment" 223 | }, 224 | { 225 | userName: "Annette Black", 226 | position: "Sales Manager", 227 | userEmail: "dolores@example.com", 228 | experience: "Dec 3", 229 | location: "location", 230 | employment: "employment" 231 | }, 232 | { 233 | userName: "Annette Black", 234 | position: "Sales Manager", 235 | userEmail: "dolores@example.com", 236 | experience: "Dec 2", 237 | location: "location", 238 | employment: "employment" 239 | }, 240 | { 241 | userName: "Annette Black", 242 | position: "Lorem!", 243 | userEmail: "dolores@example.com", 244 | experience: "Dec 2", 245 | location: "location", 246 | employment: "employment" 247 | }, 248 | { 249 | userName: "Annette Black", 250 | position: "Holiday Special!", 251 | userEmail: "dolores@example.com", 252 | experience: "11:00 AM", 253 | location: "location", 254 | employment: "employment" 255 | }, 256 | { 257 | userName: "Annette Black", 258 | position: "Lorem!", 259 | userEmail: "dolores@example.com", 260 | experience: "9:00 AM ", 261 | location: "location", 262 | employment: "employment" 263 | }, 264 | { 265 | userName: "Annette Black", 266 | position: "Codepen!", 267 | userEmail: "dolores@example.com", 268 | experience: "Dec 3", 269 | location: "location", 270 | employment: "employment" 271 | }, 272 | { 273 | userName: "Annette Black", 274 | position: "Lorem!", 275 | userEmail: "dolores@example.com", 276 | experience: "Dec 2", 277 | location: "location", 278 | employment: "employment" 279 | }, 280 | ]; 281 | return ( 282 |
283 | {/* */} 284 | 285 |
286 | ); 287 | }; 288 | 289 | export default Emails; 290 | -------------------------------------------------------------------------------- /components/LeftSide.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import Image from "next/image"; 4 | import SideBtn from "./SideBtn"; 5 | import Link from "next/link"; 6 | import { useEffect, useState } from "react"; 7 | import { signIn, signOut, useSession, getProviders } from "next-auth/react"; 8 | 9 | const LeftSide = () => { 10 | const { data: session } = useSession(); 11 | 12 | const [providers, setProviders] = useState(null); 13 | const [toggleDropdown, setToggleDropdown] = useState(false); 14 | 15 | useEffect(() => { 16 | (async () => { 17 | const res = await getProviders(); 18 | setProviders(res); 19 | })(); 20 | }, []); 21 | 22 | const buttons = [ 23 | { 24 | icon: ( 25 | Logo 32 | ), 33 | title: "Dashboard", 34 | path: "/emails", 35 | }, 36 | { 37 | icon: ( 38 | Logo 45 | ), 46 | title: "Inbox", 47 | path: "/emails", 48 | }, 49 | { 50 | icon: ( 51 | Logo 58 | ), 59 | title: "Calendar", 60 | path: "/emails", 61 | }, 62 | { 63 | icon: ( 64 | Logo 71 | ), 72 | title: "Employees", 73 | path: "/emails", 74 | }, 75 | { 76 | icon: ( 77 | Logo 84 | ), 85 | title: "Payroll", 86 | path: "/emails", 87 | }, 88 | { 89 | icon: ( 90 | Logo 97 | ), 98 | title: "Documents", 99 | path: "/emails", 100 | }, 101 | { 102 | icon: ( 103 | Logo 110 | ), 111 | title: "Departments", 112 | path: "/emails", 113 | }, 114 | { 115 | icon: ( 116 | Logo 123 | ), 124 | title: "Jobs", 125 | path: "/emails", 126 | }, 127 | { 128 | icon: ( 129 | Logo 136 | ), 137 | title: "Candidates", 138 | path: "/emails", 139 | }, 140 | { 141 | icon: ( 142 | Logo 149 | ), 150 | title: "Profile", 151 | path: "/emails", 152 | }, 153 | { 154 | icon: ( 155 | Logo 162 | ), 163 | title: "Settings", 164 | path: "/emails", 165 | }, 166 | { 167 | icon: ( 168 | Logo 175 | ), 176 | title: "Help", 177 | path: "/emails", 178 | }, 179 | ]; 180 | 181 | return ( 182 |
183 | {/* Left Section */} 184 | 185 |
186 |
187 | Logo 194 |
195 | HR Buddys 196 |
197 | 198 | {/* Buttons */} 199 |
200 | 201 | {buttons.map((button, index) => ( 202 | 209 | 214 | 215 | ))} 216 |
217 | 218 | {/* Mobile Navigation */} 219 |
220 | {session?.user ? ( 221 |
222 |
223 | profile setToggleDropdown(!toggleDropdown)} 230 | /> 231 |
232 |

236 | {session?.user.name} 237 |

238 |

239 | {session?.user.email} 240 |

241 |
242 | 243 | {toggleDropdown && ( 244 |
245 | {/* setToggleDropdown(false)} 249 | > 250 | My Profile 251 | 252 | setToggleDropdown(false)} 256 | > 257 | Create Prompt 258 | */} 259 | 269 |
270 | )} 271 |
272 |
273 | ) : ( 274 | <> 275 |
276 | {providers && 277 | Object.values(providers).map((provider) => ( 278 | 288 | ))} 289 |
290 | 291 | )} 292 |
293 | 294 |
295 | ); 296 | }; 297 | 298 | export default LeftSide; 299 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?f:wght@100;200;300;400;500;600;700;800;900&display=swap"); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | /* 8 | Note: The styles for this gradient grid background is heavily inspired by the creator of this amazing site (https://dub.sh) – all credits go to them! 9 | */ 10 | 11 | .main { 12 | width: 100vw; 13 | min-height: 100vh; 14 | position: fixed; 15 | display: flex; 16 | justify-content: center; 17 | padding: 120px 24px 160px; 18 | } 19 | 20 | .main:before { 21 | background: radial-gradient(circle, rgba(2, 0, 36, 0) 0, #fafafa 100%); 22 | position: absolute; 23 | content: ""; 24 | z-index: 2; 25 | width: 100%; 26 | height: 100%; 27 | top: 0; 28 | } 29 | 30 | .main:after { 31 | content: ""; 32 | background-image: url("/assets/images/grid.svg"); 33 | z-index: 1; 34 | position: absolute; 35 | width: 100%; 36 | height: 100%; 37 | top: 0; 38 | opacity: 0.4; 39 | filter: invert(1); 40 | } 41 | 42 | .form-container { 43 | max-width: 800px; /* Adjust the width as needed */ 44 | max-height: 400px; /* Adjust the height as needed */ 45 | overflow-y: auto; 46 | } 47 | 48 | 49 | 50 | .sidebar { 51 | position: fixed; 52 | top: 0; 53 | right: -800px; 54 | /* was .300 */ 55 | width: 800px; 56 | height: 100vh; 57 | background-color: #f2f2f2; 58 | transition: right 0.3s ease; 59 | } 60 | 61 | .sidebar.open { 62 | right: 0; 63 | } 64 | 65 | .sidebar-content { 66 | padding-left: 10px; 67 | padding-top: 30px; 68 | padding-right: 20px; 69 | padding-bottom: 20px; 70 | padding-left: 15; 71 | box-sizing: border-box; 72 | height: 100%; 73 | overflow-y: scroll; 74 | } 75 | 76 | .menu-icon { 77 | cursor: pointer; 78 | display: flex; 79 | flex-direction: column; 80 | justify-content: space-between; 81 | width: 30px; 82 | height: 20px; 83 | z-index: 1000; 84 | transition: transform 0.3s; 85 | } 86 | 87 | .menu-icon.open { 88 | transform: translateX(-0px); /* Change '210px' to '-210px' */ 89 | } 90 | 91 | .line{ 92 | width: 100%; 93 | height: 2px; 94 | background-color: #333; 95 | transition: transform 0.3s, opacity 0.3s; 96 | } 97 | 98 | .menu-icon.open .line:nth-child(1) { 99 | transform: rotate(-45deg) translate(-4px, 4px); 100 | } 101 | 102 | .menu-icon.open .line:nth-child(2) { 103 | opacity: 0; 104 | } 105 | 106 | .menu-icon.open .line:nth-child(3) { 107 | transform: rotate(45deg) translate(-4px, -4px); 108 | } 109 | 110 | .menu { 111 | display: flex; 112 | flex-direction: column; 113 | list-style-type: none; 114 | padding: 0; 115 | margin: 0; 116 | background-color: #fff; 117 | position: fixed; 118 | top: 0; 119 | right: 0; /* Change 'left' to 'right' */ 120 | width: 750px; 121 | height: 100vh; 122 | transform: translateX(100%); /* Change '-100%' to '100%' */ 123 | transition: transform 0.3s; 124 | } 125 | 126 | .menu.open { 127 | transform: translateX(0%); 128 | } 129 | 130 | .menu li{ 131 | padding: 10px; 132 | border-bottom: 1px solid #ccc; 133 | cursor: pointer; 134 | } 135 | 136 | .dropdownnew { 137 | position: absolute; 138 | top: -100%; /* Position the dropdown above the button */ 139 | right: 0; 140 | background-color: white; 141 | width: 100%; 142 | padding: 1rem; 143 | border-radius: 0.5rem; 144 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); 145 | z-index: 10; 146 | } 147 | 148 | 149 | .gray_btn { 150 | display: flex; 151 | align-items: center; 152 | justify-content: center; 153 | border-radius: 9999px; 154 | border-width: 1px; 155 | --tw-border-opacity: 1; 156 | border-color: rgb(82 72 240 / var(--tw-text-opacity)); 157 | --tw-bg-opacity: 1; 158 | background-color: #F6F6FF; 159 | padding-top: 0.375rem; 160 | padding-bottom: 0.375rem; 161 | padding-left: 1.25rem; 162 | padding-right: 1.25rem; 163 | text-align: center; 164 | font-size: 0.875rem; 165 | line-height: 1.25rem; 166 | --tw-text-opacity: 1; 167 | color: rgb(255 255 255 / var(--tw-text-opacity)); 168 | transition-property: all; 169 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 170 | transition-duration: 150ms; 171 | } 172 | 173 | .blue_btn { 174 | display: flex; 175 | align-items: center; 176 | justify-content: center; 177 | border-radius: 9999px; 178 | border-width: 1px; 179 | --tw-border-opacity: 1; 180 | border-color: rgb(82 72 240 / var(--tw-text-opacity)); 181 | --tw-bg-opacity: 1; 182 | background-color: rgb(82 72 240 / var(--tw-text-opacity)); 183 | padding-top: 0.375rem; 184 | padding-bottom: 0.375rem; 185 | padding-left: 1.25rem; 186 | padding-right: 1.25rem; 187 | text-align: center; 188 | font-size: 0.875rem; 189 | line-height: 1.25rem; 190 | --tw-text-opacity: 1; 191 | color: rgb(255 255 255 / var(--tw-text-opacity)); 192 | transition-property: all; 193 | transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); 194 | transition-duration: 150ms; 195 | } 196 | 197 | .gradient { 198 | height: fit-content; 199 | z-index: 3; 200 | width: 100%; 201 | max-width: 640px; 202 | background-image: radial-gradient( 203 | at 27% 37%, 204 | hsla(215, 98%, 61%, 1) 0px, 205 | transparent 0% 206 | ), 207 | radial-gradient(at 97% 21%, hsla(125, 98%, 72%, 1) 0px, transparent 50%), 208 | radial-gradient(at 52% 99%, hsla(354, 98%, 61%, 1) 0px, transparent 50%), 209 | radial-gradient(at 10% 29%, hsla(256, 96%, 67%, 1) 0px, transparent 50%), 210 | radial-gradient(at 97% 96%, hsla(38, 60%, 74%, 1) 0px, transparent 50%), 211 | radial-gradient(at 33% 50%, hsla(222, 67%, 73%, 1) 0px, transparent 50%), 212 | radial-gradient(at 79% 53%, hsla(343, 68%, 79%, 1) 0px, transparent 50%); 213 | position: absolute; 214 | content: ""; 215 | width: 100%; 216 | height: 100%; 217 | filter: blur(100px) saturate(150%); 218 | top: 80px; 219 | opacity: 0.15; 220 | } 221 | 222 | @media screen and (max-width: 640px) { 223 | .main { 224 | padding: 0; 225 | } 226 | } 227 | 228 | /* Tailwind Styles */ 229 | 230 | .app { 231 | @apply relative z-10 flex justify-center items-center flex-col max-w-7xl mx-auto sm:px-16 px-6; 232 | } 233 | 234 | .black_btn { 235 | @apply rounded-full border border-black bg-black py-1.5 px-5 text-white transition-all hover:bg-white hover:text-black text-center text-sm flex items-center justify-center; 236 | } 237 | 238 | .outline_btn { 239 | @apply rounded-full border border-black bg-transparent py-1.5 px-5 text-black transition-all hover:bg-black hover:text-white text-center text-sm flex items-center justify-center; 240 | } 241 | 242 | .head_text { 243 | @apply mt-5 text-5xl font-extrabold leading-[1.15] text-black sm:text-6xl; 244 | } 245 | 246 | .orange_gradient { 247 | @apply bg-gradient-to-r from-amber-500 via-orange-600 to-yellow-500 bg-clip-text text-transparent; 248 | } 249 | 250 | .green_gradient { 251 | @apply bg-gradient-to-r from-green-400 to-green-500 bg-clip-text text-transparent; 252 | } 253 | 254 | .blue_gradient { 255 | @apply bg-gradient-to-r from-blue-600 to-cyan-600 bg-clip-text text-transparent; 256 | } 257 | 258 | .desc { 259 | @apply mt-5 text-lg text-gray-600 sm:text-xl max-w-2xl; 260 | } 261 | 262 | .search_input { 263 | @apply block w-full rounded-md border border-gray-200 bg-white py-2.5 pl-5 pr-12 text-sm shadow-lg font-medium focus:border-black focus:outline-none focus:ring-0; 264 | } 265 | 266 | .copy_btn { 267 | @apply w-7 h-7 rounded-full bg-white/10 shadow-[inset_10px_-50px_94px_0_rgb(199,199,199,0.2)] backdrop-blur flex justify-center items-center; 268 | } 269 | 270 | .glassmorphism { 271 | @apply rounded-xl border border-gray-200 bg-white/20 shadow-[inset_10px_-50px_94px_0_rgb(199,199,199,0.2)] backdrop-blur p-5; 272 | } 273 | 274 | .prompt_layout { 275 | @apply space-y-6 py-8 sm:columns-2 sm:gap-6 xl:columns-3; 276 | } 277 | 278 | /* Feed Component */ 279 | .feed { 280 | @apply mt-16 mx-auto w-full max-w-xl flex justify-center items-center flex-col gap-2; 281 | } 282 | 283 | /* Form Component */ 284 | .form_textarea { 285 | @apply w-full flex rounded-lg h-[200px] mt-2 p-3 text-sm text-gray-500 outline-0; 286 | } 287 | 288 | .form_input { 289 | @apply w-full flex rounded-lg mt-2 p-3 text-sm text-gray-500 outline-0; 290 | } 291 | 292 | /* Nav Component */ 293 | .logo_text { 294 | @apply max-sm:hidden font-semibold text-lg text-black tracking-wide; 295 | } 296 | 297 | .dropdown { 298 | @apply absolute right-0 top-full mt-3 w-full p-5 rounded-lg bg-white min-w-[210px] flex flex-col gap-2 justify-end items-end; 299 | } 300 | 301 | .dropdown_link { 302 | @apply text-sm text-gray-700 hover:text-gray-500 font-medium; 303 | } 304 | 305 | /* PromptCard Component */ 306 | .prompt_card { 307 | @apply flex-1 break-inside-avoid rounded-lg border border-gray-300 bg-white/20 bg-clip-padding p-6 pb-4 backdrop-blur-lg backdrop-filter md:w-[360px] w-full h-fit; 308 | } 309 | 310 | .flex-center { 311 | @apply flex justify-center items-center; 312 | } 313 | 314 | .flex-start { 315 | @apply flex justify-start items-start; 316 | } 317 | 318 | .flex-end { 319 | @apply flex justify-end items-center; 320 | } 321 | 322 | .flex-between { 323 | @apply flex justify-between items-center; 324 | } -------------------------------------------------------------------------------- /public/assets/images/logo-text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /components/RightSideBar.jsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import Link from "next/link"; 5 | import Image from "next/image"; 6 | import { useEffect, useState } from "react"; 7 | import { useRouter, useSearchParams } from "next/navigation"; 8 | 9 | import Form from "@components/Form"; 10 | 11 | const RightSideBar = ({ 12 | isMenuOpen, 13 | selectedEmail, 14 | selectedEmailImage, 15 | toggleMenu, 16 | postID 17 | }) => { 18 | const router = useRouter(); 19 | const candidateId = postID; 20 | 21 | const [post, setPost] = useState({ 22 | userName: "", 23 | position: "", 24 | userEmail: "", 25 | experience: "", 26 | location: "", 27 | employment: "", 28 | department: "", 29 | telephone: "", 30 | }); 31 | const [submitting, setIsSubmitting] = useState(false); 32 | 33 | // This CSR data fetching 34 | useEffect(() => { 35 | // SSG: By Default the data fetching is SSG:Static Side Generation 36 | // SSR: For dynamic pages use {cache: 'no-store'}. SSR: Server Side Rendering 37 | // ISR: For dynamic data from static sides use {next:{revalidate:10}} ISR: Incremental Static generation 38 | 39 | const getPromptDetails = async () => { 40 | const response = await fetch(`/api/candidate/${candidateId}`); 41 | const data = await response.json(); 42 | 43 | setPost({ 44 | userName: data.userName, 45 | position: data.position, 46 | userEmail: data.userEmail, 47 | experience: data.experience, 48 | location: data.location, 49 | employment: data.employment, 50 | department: data.department, 51 | telephone: data.telephone, 52 | }); 53 | }; 54 | 55 | if (candidateId) getPromptDetails(); 56 | }, [candidateId]); 57 | 58 | const updateCandidate = async (e) => { 59 | e.preventDefault(); 60 | setIsSubmitting(true); 61 | 62 | // console.log("Updating candidate:", candidateId); 63 | if (!candidateId) return alert("Missing candidateId!"); 64 | 65 | try { 66 | const response = await fetch(`/api/candidate/${candidateId}`, { 67 | method: "PATCH", 68 | body: JSON.stringify({ 69 | userName: post.userName, 70 | position: post.position, 71 | userEmail: post.userEmail, 72 | experience: post.experience, 73 | location: post.location, 74 | employment: post.employment, 75 | department: post.department, 76 | telephone: post.telephone, 77 | }), 78 | }); 79 | 80 | if (response.ok) { 81 | 82 | router.push("/"); 83 | setTimeout(() => { 84 | router.push("/emails"); 85 | }, 25); 86 | 87 | 88 | } 89 | } catch (error) { 90 | console.log(error); 91 | } finally { 92 | setIsSubmitting(false); 93 | } 94 | }; 95 | 96 | return ( 97 |
98 |
99 | {/* Render the detailed view content here */} 100 |
104 |
105 |
106 |
107 |
108 | 109 | {/* when the email is clicked the sidebar slides from right */} 110 | {/* put the detailed component here */} 111 | {/* Put the check if selectedEmail exist then render this */} 112 | {selectedEmail && ( 113 |
114 | {/* First Div */} 115 |
116 | {/* leftSide */} 117 |
118 | {/* Display the image */} 119 | Guy 126 | 127 |
131 |
132 |

133 | {selectedEmail.userName} 134 |

135 |
136 | Logo 143 |

{selectedEmail.position}

144 |
145 |
146 | Logo 153 |

{selectedEmail.department}

154 |
155 |
156 | 157 |
158 |
159 | Logo 166 |

{selectedEmail.location}

167 |
168 |
169 | Logo 176 | 177 |

{selectedEmail.employment}

178 |
179 |
180 |
181 |
182 | Logo 189 | 190 |

{selectedEmail.telephone}

191 |
192 |
193 | Logo 200 | 201 |

{selectedEmail.userEmail}

202 |
203 |
204 |
205 |
206 | 207 | {/* Right Side */} 208 |
209 |
210 | Guy 217 |
218 | 219 |
220 |
221 | 225 | Edit Data 226 | 227 |
228 |
229 |
230 |
231 | 232 | {/* Second Div */} 233 |
234 |
235 | 239 | Main Information 240 | 241 |
242 |
243 | 244 | Salary Data 245 | 246 |
247 |
248 | 249 | Documents attached 250 | 251 |
252 |
253 | {/* Third Div */} 254 |
255 | {/* Left Div */} 256 |
257 |

Personal Data

258 | {/* FORM for editing */} 259 | {/*
{}} 261 | className="mt-5 w-full max-w-2xl flex flex-col gap-3 " 262 | > 263 | 276 | 277 | 291 | 292 | 306 | 320 | 321 |
322 | 323 | Cancel 324 | 325 | 326 | 333 |
334 |
*/} 335 |
342 |
343 | {/* Right Div */} 344 |
345 |

CV Preview

346 | Guy 353 |
354 |
355 |
356 | )} 357 |
358 |
359 | ); 360 | }; 361 | 362 | export default RightSideBar; 363 | --------------------------------------------------------------------------------