🚀 Online Job Portal – Project Overview
A platform where job seekers can create profiles, upload resumes, and apply for jobs, while employers/recruiters can post job listings, manage applications, and shortlist candidates.
Tech Stack
Frontend: Next.js (React-based framework), ShadCN (UI components), TailwindCSS (styling), Redux Toolkit (state management)
Backend & Database: Appwrite (auth, database, storage, serverless functions)
Hosting/Deployment: Vercel (frontend) + Appwrite Cloud/Self-hosted
For Job Seekers:
Signup/Login with Appwrite Auth
Create & update profile (resume, skills, experience)
Search & filter jobs (by role, location, salary, company)
Apply for jobs (store in Appwrite DB)
Track application status
For Employers:
Post new jobs (title, description, salary, requirements, location)
Manage job listings
View applicants & shortlist candidates
General Features:
Modern UI with ShadCN & Tailwind
State management with Redux Toolkit
Serverless functions for application workflows
Notifications (optional: email via Appwrite functions)
Role-based access (job seeker vs employer)
Key Integrations
Authentication: Appwrite (OAuth or email/password)
Database: Appwrite collections (Users, Jobs, Applications)
Storage: Appwrite file storage for resumes
UI: ShadCN components (cards, forms, modals, tables)
State: Redux Toolkit slices for user, jobs, applications
📌 Header Component
import React from "react"; import Link from "next/link"; import { Button } from "../ui/button"; import Navlinks from "./Navlinks"; import { RxHamburgerMenu } from "react-icons/rx"; import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar"; const Header = () => { return ( <header className="bg-[#2a1b3d] px-4 sm:px-10 py-3 sm:py-5 flex justify-between items-center"> {/* Logo */} <div className="logo"> <Link href={"/"}> <h1 className="text-xl sm:text-3xl font-semibold text-white"> Online Job Portal </h1> </Link> </div> {/* Navigation Links */} <Navlinks /> {/* Right Section */} <div className="flex items-center gap-4"> {/* Login / Signup Button */} <Button className="border-none bg-[#D83F87] hover:bg-[#f54698] text-white"> Sign Up </Button> {/* Example Avatar */} <Avatar className="cursor-pointer hidden"> <AvatarImage src="" alt="User" /> <AvatarFallback>U</AvatarFallback> </Avatar> {/* Hamburger Menu for Mobile */} <RxHamburgerMenu color="white" size={32} className="lg:hidden cursor-pointer" /> </div> </header> ); }; export default Header;
Hero Section Component
import React from "react";
const Herosection = () => {
return (
<section className="relative flex flex-col justify-center items-center w-full h-[450px] sm:h-[350px] p-5 md:p-10">
{/* Background Image with Overlay */}
<div className="absolute inset-0 -z-10">
<img
src="https://images.unsplash.com/photo-1516534775068-ba3e7458af70?w=1200&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OHx8b25saW5lJTIwam9ifGVufDB8fDB8fHww"
alt="Hero Background"
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-[#190572ad]" />
</div>
{/* Hero Content */}
<div className="z-10 max-w-3xl text-center">
<h1 className="text-white text-2xl md:text-3xl lg:text-4xl font-bold leading-snug">
“Connecting Employers with Top Talent Globally.”
</h1>
<h2 className="text-white text-lg md:text-2xl lg:text-3xl mt-3 leading-snug">
Join millions of job seekers finding new opportunities every day.
</h2>
{/* Searchbox Placeholder */}
{/* <div className="my-5">
<Searchbox />
</div> */}
</div>
{/* Chatbot Floating Button */}
<div className="fixed right-5 bottom-10 md:top-56 z-30 flex flex-col items-end">
{/* Example chatbot icon (replace with your actual asset) */}
<img
src="./Reddit.png"
alt="chatbot"
className="w-12 h-12 cursor-pointer hover:scale-110 transition-transform"
/>
</div>
</section>
);
};
export default Herosection;
Job Section Component
import React from "react";
import Jobcard from "./Jobcard";
import Link from "next/link";
const Jobsection = () => {
return (
<section className="bg-[#2E2835] w-full h-auto px-2 py-5 sm:p-10">
<h1 className="text-xl sm:text-3xl uppercase font-semibold text-center text-white p-10">
Recent Jobs
</h1>
<div className="job-container flex flex-wrap justify-center gap-5">
{/* Example Job Cards (Replace with dynamic jobs later) */}
<Link href="/jobs/frontend-developer_123">
<Jobcard
data={{
title: "Frontend Developer",
company: "Tech Corp",
location: "Remote",
description: "Work on modern web applications with React & Next.js.",
}}
/>
</Link>
<Link href="/jobs/backend-developer_456">
<Jobcard
data={{
title: "Backend Developer",
company: "CodeWorks",
location: "Kathmandu, Nepal",
description: "Build scalable backend services with Node.js and Django.",
}}
/>
</Link>
<Link href="/jobs/ui-ux-designer_789">
<Jobcard
data={{
title: "UI/UX Designer",
company: "Design Studio",
location: "Hybrid",
description: "Create intuitive designs and user experiences.",
}}
/>
</Link>
</div>
</section>
);
};
export default Jobsection;
Searchbox Component
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const Searchbox = () => {
return (
<div className="search flex flex-col sm:flex-row w-full max-w-4xl mx-auto border-2 border-white rounded-lg overflow-hidden">
{/* Job Title Input */}
<Input
type="text"
placeholder="Search your Job title"
className="text-white text-lg border-none outline-none flex-1 bg-transparent placeholder:text-white px-4 py-3"
/>
{/* Location Selector */}
<Select>
<SelectTrigger className="w-full sm:w-[250px] text-white text-lg border-none bg-pink-600 hover:bg-pink-700 transition-colors">
<SelectValue placeholder="Select your location" />
</SelectTrigger>
<SelectContent className="text-lg">
<SelectItem value="kathmandu">Kathmandu</SelectItem>
<SelectItem value="pokhara">Pokhara</SelectItem>
<SelectItem value="lalitpur">Lalitpur</SelectItem>
<SelectItem value="bhaktapur">Bhaktapur</SelectItem>
<SelectItem value="remote">Remote</SelectItem>
</SelectContent>
</Select>
{/* Search Button */}
<Button className="w-full sm:w-[200px] bg-[#D83F87] hover:bg-[#f54698] text-white text-lg font-medium transition-colors">
Search
</Button>
</div>
);
};
export default Searchbox;
Navigation Links Component
import React from "react";
import { Button } from "../ui/button";
import Link from "next/link";
const Navlinks = () => {
const Linksbtn = [
{ name: "about", link: "/" },
{ name: "skill certification", link: "/" },
{ name: "find job", link: "/" },
{ name: "post job", link: "/" },
{ name: "contact us", link: "/" },
];
return (
<nav className="text-white flex flex-col lg:flex-row lg:items-center
gap-5 lg:gap-8 bg-[#af8dff20] lg:bg-transparent backdrop-blur-md px-10 py-8 lg:p-0
rounded-lg lg:rounded-none fixed lg:relative top-0 right-0 w-[250px] lg:w-auto h-full lg:h-auto z-50 lg:z-auto transition-all">
{Linksbtn.map((data) => (
<Link href={data.link} key={data.name}>
<Button
variant="link"
className="text-white text-lg lg:text-base capitalize hover:text-pink-500
transition-colors"
>
{data.name}
</Button>
</Link>
))}
</nav>
);
};
export default Navlinks;
Footer Component
import React from "react";
import Link from "next/link";
import { FaFacebookF, FaTwitter, FaLinkedinIn } from "react-icons/fa";
const Footer = () => {
return (
<footer className="bg-[#2a1b3d] text-white py-10 px-6 mt-10">
<div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-8">
{/* About */}
<div>
<h2 className="text-xl font-semibold mb-3">Online Job Portal</h2>
<p className="text-sm text-gray-300">
Helping job seekers and employers connect with ease.
</p>
</div>
{/* Quick Links */}
<div>
<h2 className="text-lg font-semibold mb-3">Quick Links</h2>
<ul className="space-y-2 text-sm">
<li>
<Link href="/" className="hover:text-pink-400">Home</Link>
</li>
<li>
<Link href="/jobs" className="hover:text-pink-400">Find Jobs</Link>
</li>
<li>
<Link href="/post-job" className="hover:text-pink-400">Post a Job</Link>
</li>
<li>
<Link href="/contact" className="hover:text-pink-400">Contact Us</Link>
</li>
</ul>
</div>
{/* Social Media */}
<div>
<h2 className="text-lg font-semibold mb-3">Follow Us</h2>
<div className="flex gap-4">
<Link href="https://facebook.com" target="_blank" className="hover:text-pink-400">
<FaFacebookF size={20} />
</Link>
<Link href="https://twitter.com" target="_blank" className="hover:text-pink-400">
<FaTwitter size={20} />
</Link>
<Link href="https://linkedin.com" target="_blank" className="hover:text-pink-400">
<FaLinkedinIn size={20} />
</Link>
</div>
</div>
</div>
<div className="text-center text-gray-400 text-sm mt-8">
© {new Date().getFullYear()} Online Job Portal. All rights reserved.
</div>
</footer>
);
};
export default Footer;
Chatbot Component
import React, { useState } from "react";
import { BsChatDotsFill } from "react-icons/bs";
const Chatbot = () => {
const [isOpen, setIsOpen] = useState(false);
const [messages, setMessages] = useState<{ sender: string; text: string }[]>([]);
const [input, setInput] = useState("");
const toggleChat = () => setIsOpen(!isOpen);
const handleSend = () => {
if (input.trim() === "") return;
setMessages([...messages, { sender: "user", text: input }]);
setInput("");
// Fake bot reply
setTimeout(() => {
setMessages((prev) => [...prev, { sender: "bot", text: "Hello! How can I help you today?" }]);
}, 1000);
};
return (
<div className="fixed bottom-6 right-6">
{/* Chat Icon */}
<button
onClick={toggleChat}
className="bg-[#D83F87] p-4 rounded-full shadow-lg text-white hover:bg-[#f54698] transition"
>
<BsChatDotsFill size={24} />
</button>
{/* Chat Window */}
{isOpen && (
<div className="bg-white w-80 h-96 shadow-xl rounded-xl flex flex-col mt-3">
{/* Header */}
<div className="bg-[#2a1b3d] text-white px-4 py-3 rounded-t-xl">
<h3 className="font-semibold">Job Portal Assistant</h3>
</div>
{/* Messages */}
<div className="flex-1 p-3 overflow-y-auto text-sm space-y-2">
{messages.map((msg, i) => (
<div
key={i}
className={`p-2 rounded-lg ${
msg.sender === "user"
? "bg-[#D83F87] text-white self-end"
: "bg-gray-200 text-gray-800 self-start"
}`}
>
{msg.text}
</div>
))}
</div>
{/* Input */}
<div className="p-3 border-t flex gap-2">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
placeholder="Type a message..."
className="flex-1 border rounded-lg px-3 py-2 text-sm focus:outline-none"
/>
<button
onClick={handleSend}
className="bg-[#D83F87] hover:bg-[#f54698] text-white px-3 rounded-lg"
>
Send
</button>
</div>
</div>
)}
</div>
);
};
export default Chatbot;
Category Signup Component
import { GiCrossMark } from "react-icons/gi"; const Categorysignup = () => { return ( <div className="fixed inset-0 backdrop-blur-md z-50 flex justify-center items-center p-5"> {/* Form Container */} <div className="w-full sm:w-[500px] bg-[#af8dff65] p-5 sm:p-10 rounded-lg backdrop-blur-md relative"> {/* Close Button */} <GiCrossMark className="text-3xl text-white cursor-pointer hover:text-red-500 absolute right-4 top-4" /> <h2 className="text-center font-bold text-white text-3xl mb-5"> Select Signup Category </h2> {/* Jobseeker Option */} <div className="w-full sm:w-[80%] h-[100px] rounded-lg px-4 py-4 bg-white my-6 mx-auto flex items-center justify-center text-center text-xl text-pink-500 cursor-pointer hover:bg-pink-50 transition-colors"> <div> <p className="font-semibold">Jobseeker</p> <p className="text-sm mt-1">Create a free account to apply!</p> </div> </div> {/* Job Provider Option */} <div className="w-full sm:w-[80%] h-[100px] rounded-lg px-4 py-4 bg-white my-6 mx-auto flex items-center justify-center text-center text-xl text-pink-500 cursor-pointer hover:bg-pink-50 transition-colors"> <div> <p className="font-semibold">Job Provider</p> <p className="text-sm mt-1">Create a free account to post vacancies!</p> </div> </div> </div> </div> ); }; export default Categorysignup;import React from "react";
Login Form
import React, { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
const Login = () => {
const [form, setForm] = useState({ email: "", password: "" });
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log("Login Data:", form);
};
return (
<div className="max-w-md mx-auto mt-16 bg-white shadow-lg rounded-xl p-6">
<h2 className="text-2xl font-semibold mb-4 text-center text-gray-900">Welcome Back</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<Input
type="email"
name="email"
placeholder="Email Address"
value={form.email}
onChange={handleChange}
required
/>
<Input
type="password"
name="password"
placeholder="Password"
value={form.password}
onChange={handleChange}
required
/>
<Button type="submit" className="w-full bg-[#D83F87] hover:bg-[#f54698] text-white">
Login
</Button>
</form>
</div>
);
};
export default Login;
No comments:
If you have any doubts please let's me know