├── 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 |
7 |
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 |
8 | {modifiedTitle}
9 | {logo}
10 |
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 |
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 |
51 | {/* Display the image */}
52 |
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 |
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 |
21 | ),
22 | // logo: ,
23 | title: "FULL NAME",
24 | css: "hover:text-yellow-700 focus:text-yellow-500",
25 | },
26 | {
27 | logo: (
28 |
35 | ),
36 |
37 | // logo: ,
38 | title: "POSITION",
39 | css: "hover:text-purple-700 focus:text-purple-500",
40 | },
41 | {
42 | logo: (
43 |
50 | ),
51 |
52 | // logo: ,
53 | title: "EMAIL",
54 | css: "hover:text-green-700 focus:text-green-500",
55 | },
56 | {
57 | logo: (
58 |
65 | ),
66 |
67 | // logo: ,
68 | title: "EXPERIENCE",
69 | css: "hover:text-gray-700 focus:text-gray-500",
70 | },
71 | {
72 | logo: (
73 |
80 | ),
81 |
82 | // logo: ,
83 | title: "LOCATION",
84 | css: "hover:text-blue-700 focus:text-blue-500",
85 | },
86 | {
87 | logo: (
88 |
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 |
132 | Filter
133 |
134 |
135 | By relevance
136 |
137 | {session?.user ? (
138 | + Add New
139 | ):(
140 | <>>
141 | )}
142 |
143 |
144 |
145 |
146 | {/* Categories */}
147 |
148 |
149 |
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 |
32 | ),
33 | title: "Dashboard",
34 | path: "/emails",
35 | },
36 | {
37 | icon: (
38 |
45 | ),
46 | title: "Inbox",
47 | path: "/emails",
48 | },
49 | {
50 | icon: (
51 |
58 | ),
59 | title: "Calendar",
60 | path: "/emails",
61 | },
62 | {
63 | icon: (
64 |
71 | ),
72 | title: "Employees",
73 | path: "/emails",
74 | },
75 | {
76 | icon: (
77 |
84 | ),
85 | title: "Payroll",
86 | path: "/emails",
87 | },
88 | {
89 | icon: (
90 |
97 | ),
98 | title: "Documents",
99 | path: "/emails",
100 | },
101 | {
102 | icon: (
103 |
110 | ),
111 | title: "Departments",
112 | path: "/emails",
113 | },
114 | {
115 | icon: (
116 |
123 | ),
124 | title: "Jobs",
125 | path: "/emails",
126 | },
127 | {
128 | icon: (
129 |
136 | ),
137 | title: "Candidates",
138 | path: "/emails",
139 | },
140 | {
141 | icon: (
142 |
149 | ),
150 | title: "Profile",
151 | path: "/emails",
152 | },
153 | {
154 | icon: (
155 |
162 | ),
163 | title: "Settings",
164 | path: "/emails",
165 | },
166 | {
167 | icon: (
168 |
175 | ),
176 | title: "Help",
177 | path: "/emails",
178 | },
179 | ];
180 |
181 | return (
182 |
183 | {/* Left Section */}
184 |
185 |
186 |
187 |
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 |
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 | {
262 | setToggleDropdown(false);
263 | signOut();
264 | }}
265 | className="mt-5 w-full blue_btn"
266 | >
267 | Sign Out
268 |
269 |
270 | )}
271 |
272 |
273 | ) : (
274 | <>
275 |
276 | {providers &&
277 | Object.values(providers).map((provider) => (
278 | {
282 | signIn(provider.id);
283 | }}
284 | className="blue_btn "
285 | >
286 | Sign in
287 |
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 |
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 |
126 |
127 |
131 |
132 |
133 | {selectedEmail.userName}
134 |
135 |
136 |
143 |
{selectedEmail.position}
144 |
145 |
146 |
153 |
{selectedEmail.department}
154 |
155 |
156 |
157 |
158 |
159 |
166 |
{selectedEmail.location}
167 |
168 |
169 |
176 |
177 |
{selectedEmail.employment}
178 |
179 |
180 |
181 |
182 |
189 |
190 |
{selectedEmail.telephone}
191 |
192 |
193 |
200 |
201 |
{selectedEmail.userEmail}
202 |
203 |
204 |
205 |
206 |
207 | {/* Right Side */}
208 |
209 |
210 |
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 | {/*
*/}
335 |
342 |
343 | {/* Right Div */}
344 |
345 |
CV Preview
346 |
353 |
354 |
355 |
356 | )}
357 |
358 |
359 | );
360 | };
361 |
362 | export default RightSideBar;
363 |
--------------------------------------------------------------------------------