Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf649b28ae | |||
| 546d020d5a | |||
| 41ba7a7546 | |||
| b0a6f0dbb7 | |||
| 8b60177ad1 | |||
| e800be26cd | |||
| 9d8b850abc | |||
| fbcc591b0e | |||
| bc899d6e44 | |||
| f69459c79e | |||
| c696694318 | |||
| 36b2c55b8d | |||
| 0f5ae59296 | |||
| 352af2b1b6 | |||
| 44a13a952f | |||
| abf5473f6e | |||
| 00b122afac | |||
| 03f62ec21d | |||
| 3782a4c02d | |||
| ef348208d8 | |||
| 0d569ac586 |
@@ -1,44 +1,110 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
import MetricCardTen from "@/components/sections/metrics/MetricCardTen";
|
|
||||||
import FeatureBento from "@/components/sections/feature/FeatureBento";
|
|
||||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
import {
|
import { Users, Briefcase, BarChart3, ChevronDown, Plus, Trash2, Edit2 } from "lucide-react";
|
||||||
Users,
|
|
||||||
BarChart3,
|
|
||||||
Shield,
|
|
||||||
Activity,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: "Dashboard", id: "/admin" },
|
{ name: "Dashboard", id: "/admin" },
|
||||||
{ name: "Jobs", id: "/admin#jobs" },
|
{ name: "Jobs", id: "/admin" },
|
||||||
{ name: "Users", id: "/admin#users" },
|
{ name: "Users", id: "/admin" },
|
||||||
{ name: "Moderation", id: "/admin#moderation" },
|
{ name: "Analytics", id: "/admin" },
|
||||||
{ name: "Analytics", id: "/admin#analytics" },
|
{ name: "Home", id: "/" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const footerColumns = [
|
const footerColumns = [
|
||||||
{
|
{
|
||||||
title: "Admin", items: [
|
title: "Product", items: [
|
||||||
{ label: "Dashboard", href: "/admin" },
|
{ label: "Search Jobs", href: "/search" },
|
||||||
{ label: "Settings", href: "/admin#settings" },
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
{ label: "Reports", href: "/admin#reports" },
|
{ label: "Browse by Province", href: "#provinces" },
|
||||||
|
{ label: "For Employers", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Quick Links", items: [
|
title: "Company", items: [
|
||||||
{ label: "Help", href: "#" },
|
{ label: "About Jobee", href: "#about" },
|
||||||
|
{ label: "Careers", href: "#" },
|
||||||
|
{ label: "Contact Us", href: "#contact" },
|
||||||
|
{ label: "Blog", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources", items: [
|
||||||
|
{ label: "Privacy Policy", href: "#" },
|
||||||
|
{ label: "Terms of Service", href: "#" },
|
||||||
|
{ label: "FAQ", href: "#" },
|
||||||
{ label: "Support", href: "#" },
|
{ label: "Support", href: "#" },
|
||||||
{ label: "Documentation", href: "#" },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function AdminPage() {
|
interface Job {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
company: string;
|
||||||
|
location: string;
|
||||||
|
status: "active" | "closed";
|
||||||
|
applications: number;
|
||||||
|
postedDate: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
role: "job_seeker" | "employer";
|
||||||
|
joinDate: string;
|
||||||
|
status: "active" | "inactive";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AnalyticsData {
|
||||||
|
totalJobs: number;
|
||||||
|
totalUsers: number;
|
||||||
|
activeApplications: number;
|
||||||
|
successfulPlacements: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AdminDashboard() {
|
||||||
|
const [activeTab, setActiveTab] = useState<"dashboard" | "jobs" | "users">("dashboard");
|
||||||
|
const [jobs, setJobs] = useState<Job[]>([
|
||||||
|
{
|
||||||
|
id: "1", title: "Senior React Developer", company: "TechCorp", location: "Amsterdam", status: "active", applications: 24,
|
||||||
|
postedDate: "2025-01-10"},
|
||||||
|
{
|
||||||
|
id: "2", title: "UX Designer", company: "DesignStudio", location: "Rotterdam", status: "active", applications: 18,
|
||||||
|
postedDate: "2025-01-08"},
|
||||||
|
{
|
||||||
|
id: "3", title: "Backend Engineer", company: "DataSystems", location: "Utrecht", status: "closed", applications: 42,
|
||||||
|
postedDate: "2024-12-20"},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [users, setUsers] = useState<User[]>([
|
||||||
|
{
|
||||||
|
id: "1", name: "Alice Johnson", email: "alice@example.com", role: "job_seeker", joinDate: "2024-11-15", status: "active"},
|
||||||
|
{
|
||||||
|
id: "2", name: "Bob Smith", email: "bob@example.com", role: "employer", joinDate: "2024-10-20", status: "active"},
|
||||||
|
{
|
||||||
|
id: "3", name: "Carol White", email: "carol@example.com", role: "job_seeker", joinDate: "2024-09-05", status: "inactive"},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [analytics] = useState<AnalyticsData>({
|
||||||
|
totalJobs: 156,
|
||||||
|
totalUsers: 3245,
|
||||||
|
activeApplications: 892,
|
||||||
|
successfulPlacements: 142,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleDeleteJob = (id: string) => {
|
||||||
|
setJobs(jobs.filter((job) => job.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteUser = (id: string) => {
|
||||||
|
setUsers(users.filter((user) => user.id !== id));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="text-stagger"
|
defaultButtonVariant="text-stagger"
|
||||||
@@ -57,178 +123,224 @@ export default function AdminPage() {
|
|||||||
brandName="Jobee Admin"
|
brandName="Jobee Admin"
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
button={{
|
button={{
|
||||||
text: "Logout", onClick: () => console.log("logout"),
|
text: "Logout", onClick: () => console.log("Logout"),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="jobs" data-section="jobs" className="pt-20">
|
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 py-16 px-4">
|
||||||
<MetricCardTen
|
<div className="max-w-7xl mx-auto">
|
||||||
title="Job Management"
|
{/* Header */}
|
||||||
description="Monitor and manage all job postings across the platform"
|
<div className="mb-12">
|
||||||
tag="Active Jobs"
|
<h1 className="text-4xl font-bold text-slate-900 mb-2">Admin Dashboard</h1>
|
||||||
textboxLayout="default"
|
<p className="text-slate-600">Manage jobs, users, and view analytics</p>
|
||||||
useInvertedBackground={false}
|
</div>
|
||||||
animationType="slide-up"
|
|
||||||
metrics={[
|
|
||||||
{
|
|
||||||
id: "1", title: "Senior Software Engineer, Backend", subtitle: "Amsterdam, Netherlands · Full-time · Remote eligible", category: "Engineering", value: "Posted 2 days ago", buttons: [
|
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2", title: "Product Manager, Enterprise", subtitle: "Rotterdam, Netherlands · Full-time", category: "Product", value: "Posted 5 days ago", buttons: [
|
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "3", title: "UX Designer, B2B", subtitle: "Utrecht, Netherlands · Full-time", category: "Design", value: "Posted 1 week ago", buttons: [
|
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "4", title: "Data Scientist, ML Platform", subtitle: "Remote · Full-time", category: "Data", value: "Posted 10 days ago", buttons: [
|
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "5", title: "DevOps Engineer, Infrastructure", subtitle: "Amsterdam, Netherlands · Full-time · Remote", category: "Infrastructure", value: "Posted 3 weeks ago", buttons: [
|
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "6", title: "Marketing Manager, Growth", subtitle: "The Hague, Netherlands · Full-time", category: "Marketing", value: "Posted 1 month ago", buttons: [
|
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="users" data-section="users">
|
{/* Tab Navigation */}
|
||||||
<MetricCardTen
|
<div className="flex gap-4 mb-8">
|
||||||
title="User Management"
|
<button
|
||||||
description="Overview of registered users, job seekers, and employers"
|
onClick={() => setActiveTab("dashboard")}
|
||||||
tag="Active Users"
|
className={`px-6 py-3 rounded-lg font-semibold transition-all ${
|
||||||
textboxLayout="default"
|
activeTab === "dashboard"
|
||||||
useInvertedBackground={true}
|
? "bg-blue-600 text-white shadow-lg"
|
||||||
animationType="slide-up"
|
: "bg-white text-slate-700 hover:bg-slate-100"
|
||||||
metrics={[
|
}`}
|
||||||
{
|
>
|
||||||
id: "1", title: "Sarah van der Berg", subtitle: "Amsterdam, Netherlands · Software Developer", category: "Job Seeker", value: "Active", buttons: [
|
<BarChart3 className="inline mr-2" size={20} />
|
||||||
{ text: "View Profile", href: "#" },
|
Dashboard
|
||||||
{ text: "Manage", href: "#" },
|
</button>
|
||||||
],
|
<button
|
||||||
},
|
onClick={() => setActiveTab("jobs")}
|
||||||
{
|
className={`px-6 py-3 rounded-lg font-semibold transition-all ${
|
||||||
id: "2", title: "Acme Corporation", subtitle: "Rotterdam, Netherlands · Tech Company", category: "Employer", value: "Verified", buttons: [
|
activeTab === "jobs"
|
||||||
{ text: "View", href: "#" },
|
? "bg-blue-600 text-white shadow-lg"
|
||||||
{ text: "Manage", href: "#" },
|
: "bg-white text-slate-700 hover:bg-slate-100"
|
||||||
],
|
}`}
|
||||||
},
|
>
|
||||||
{
|
<Briefcase className="inline mr-2" size={20} />
|
||||||
id: "3", title: "Jan Pieterzoon", subtitle: "Utrecht, Netherlands · HR Manager", category: "Employer", value: "Active", buttons: [
|
Jobs ({jobs.length})
|
||||||
{ text: "View Profile", href: "#" },
|
</button>
|
||||||
{ text: "Manage", href: "#" },
|
<button
|
||||||
],
|
onClick={() => setActiveTab("users")}
|
||||||
},
|
className={`px-6 py-3 rounded-lg font-semibold transition-all ${
|
||||||
{
|
activeTab === "users"
|
||||||
id: "4", title: "Emma Dijkstra", subtitle: "The Hague, Netherlands · Marketing Specialist", category: "Job Seeker", value: "Active", buttons: [
|
? "bg-blue-600 text-white shadow-lg"
|
||||||
{ text: "View Profile", href: "#" },
|
: "bg-white text-slate-700 hover:bg-slate-100"
|
||||||
{ text: "Manage", href: "#" },
|
}`}
|
||||||
],
|
>
|
||||||
},
|
<Users className="inline mr-2" size={20} />
|
||||||
]}
|
Users ({users.length})
|
||||||
/>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="analytics" data-section="analytics">
|
{/* Dashboard Tab */}
|
||||||
<FeatureBento
|
{activeTab === "dashboard" && (
|
||||||
title="Platform Analytics & Insights"
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
||||||
description="Monitor key metrics and platform performance across all regions"
|
<div className="bg-white rounded-lg p-6 shadow-md">
|
||||||
tag="Real-time Data"
|
<div className="flex items-center justify-between">
|
||||||
tagIcon={Activity}
|
<div>
|
||||||
textboxLayout="default"
|
<p className="text-slate-600 text-sm font-medium">Total Jobs</p>
|
||||||
useInvertedBackground={false}
|
<p className="text-3xl font-bold text-slate-900 mt-2">{analytics.totalJobs}</p>
|
||||||
animationType="slide-up"
|
</div>
|
||||||
carouselMode="buttons"
|
<Briefcase className="text-blue-600" size={40} />
|
||||||
features={[
|
</div>
|
||||||
{
|
</div>
|
||||||
title: "Job Applications Trend", description: "Track weekly application volume and trends", bentoComponent: "line-chart"},
|
<div className="bg-white rounded-lg p-6 shadow-md">
|
||||||
{
|
<div className="flex items-center justify-between">
|
||||||
title: "Revenue by Province", description: "Revenue distribution across Dutch provinces", bentoComponent: "animated-bar-chart"},
|
<div>
|
||||||
{
|
<p className="text-slate-600 text-sm font-medium">Total Users</p>
|
||||||
title: "User Growth Metrics", description: "Active users and engagement statistics", bentoComponent: "icon-info-cards", items: [
|
<p className="text-3xl font-bold text-slate-900 mt-2">{analytics.totalUsers}</p>
|
||||||
{ icon: Users, label: "Total Users", value: "24,500+" },
|
</div>
|
||||||
{ icon: Activity, label: "Job Postings", value: "3,240" },
|
<Users className="text-green-600" size={40} />
|
||||||
{ icon: TrendingUp, label: "Monthly Growth", value: "12.5%" },
|
</div>
|
||||||
],
|
</div>
|
||||||
},
|
<div className="bg-white rounded-lg p-6 shadow-md">
|
||||||
{
|
<div className="flex items-center justify-between">
|
||||||
title: "Geographic Distribution", description: "Job seeker and employer locations worldwide", bentoComponent: "map"},
|
<div>
|
||||||
{
|
<p className="text-slate-600 text-sm font-medium">Active Applications</p>
|
||||||
title: "Popular Job Categories", description: "Most sought-after job positions and skills", bentoComponent: "marquee", centerIcon: Activity,
|
<p className="text-3xl font-bold text-slate-900 mt-2">{analytics.activeApplications}</p>
|
||||||
variant: "text", texts: [
|
</div>
|
||||||
"Software Engineering", "Product Management", "Data Science", "UX Design", "Marketing", "Sales"],
|
<ChevronDown className="text-purple-600" size={40} />
|
||||||
},
|
</div>
|
||||||
{
|
</div>
|
||||||
title: "System Status Overview", description: "Platform health and service monitoring", bentoComponent: "3d-stack-cards", items: [
|
<div className="bg-white rounded-lg p-6 shadow-md">
|
||||||
{
|
<div className="flex items-center justify-between">
|
||||||
icon: Activity,
|
<div>
|
||||||
title: "API Health", subtitle: "99.9% Uptime", detail: "All systems operational"},
|
<p className="text-slate-600 text-sm font-medium">Successful Placements</p>
|
||||||
{
|
<p className="text-3xl font-bold text-slate-900 mt-2">{analytics.successfulPlacements}</p>
|
||||||
icon: BarChart3,
|
</div>
|
||||||
title: "Database", subtitle: "256 GB", detail: "45% capacity used"},
|
<BarChart3 className="text-orange-600" size={40} />
|
||||||
{
|
</div>
|
||||||
icon: Shield,
|
</div>
|
||||||
title: "Security", subtitle: "Secure", detail: "No active threats"},
|
</div>
|
||||||
],
|
)}
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="moderation" data-section="moderation">
|
{/* Jobs Tab */}
|
||||||
<MetricCardTen
|
{activeTab === "jobs" && (
|
||||||
title="Content Moderation"
|
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
description="Review and manage flagged content, reports, and user complaints"
|
<div className="p-6 border-b border-slate-200 flex justify-between items-center">
|
||||||
tag="Flagged Content"
|
<h2 className="text-2xl font-bold text-slate-900">Job Management</h2>
|
||||||
textboxLayout="default"
|
<button className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-all">
|
||||||
useInvertedBackground={true}
|
<Plus size={20} />
|
||||||
animationType="slide-up"
|
Add Job
|
||||||
metrics={[
|
</button>
|
||||||
{
|
</div>
|
||||||
id: "1", title: "Job Post: Suspicious Salary Offer", subtitle: "Posted by Employer XYZ · Flag reason: Unusually high salary", category: "Review", value: "Pending", buttons: [
|
<div className="overflow-x-auto">
|
||||||
{ text: "Review", href: "#" },
|
<table className="w-full">
|
||||||
{ text: "Action", href: "#" },
|
<thead className="bg-slate-50 border-b border-slate-200">
|
||||||
],
|
<tr>
|
||||||
},
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Title</th>
|
||||||
{
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Company</th>
|
||||||
id: "2", title: "User Report: Inappropriate Profile", subtitle: "Report from user about profile content violation", category: "Report", value: "2 days ago", buttons: [
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Location</th>
|
||||||
{ text: "Investigate", href: "#" },
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Status</th>
|
||||||
{ text: "Resolve", href: "#" },
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Applications</th>
|
||||||
],
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Actions</th>
|
||||||
},
|
</tr>
|
||||||
{
|
</thead>
|
||||||
id: "3", title: "Automated Alert: Spam Job Posting", subtitle: "System detected duplicate job posts from same employer", category: "Alert", value: "Flagged", buttons: [
|
<tbody>
|
||||||
{ text: "Review", href: "#" },
|
{jobs.map((job) => (
|
||||||
{ text: "Remove", href: "#" },
|
<tr key={job.id} className="border-b border-slate-200 hover:bg-slate-50 transition-colors">
|
||||||
],
|
<td className="px-6 py-4 text-slate-900 font-medium">{job.title}</td>
|
||||||
},
|
<td className="px-6 py-4 text-slate-600">{job.company}</td>
|
||||||
]}
|
<td className="px-6 py-4 text-slate-600">{job.location}</td>
|
||||||
/>
|
<td className="px-6 py-4">
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||||
|
job.status === "active"
|
||||||
|
? "bg-green-100 text-green-800"
|
||||||
|
: "bg-red-100 text-red-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{job.status.charAt(0).toUpperCase() + job.status.slice(1)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 text-slate-600">{job.applications}</td>
|
||||||
|
<td className="px-6 py-4 flex gap-2">
|
||||||
|
<button className="p-2 text-blue-600 hover:bg-blue-50 rounded transition-colors">
|
||||||
|
<Edit2 size={18} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteJob(job.id)}
|
||||||
|
className="p-2 text-red-600 hover:bg-red-50 rounded transition-colors"
|
||||||
|
>
|
||||||
|
<Trash2 size={18} />
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Users Tab */}
|
||||||
|
{activeTab === "users" && (
|
||||||
|
<div className="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
|
<div className="p-6 border-b border-slate-200 flex justify-between items-center">
|
||||||
|
<h2 className="text-2xl font-bold text-slate-900">User Management</h2>
|
||||||
|
<button className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-all">
|
||||||
|
<Plus size={20} />
|
||||||
|
Add User
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="w-full">
|
||||||
|
<thead className="bg-slate-50 border-b border-slate-200">
|
||||||
|
<tr>
|
||||||
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Name</th>
|
||||||
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Email</th>
|
||||||
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Role</th>
|
||||||
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Join Date</th>
|
||||||
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Status</th>
|
||||||
|
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-900">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{users.map((user) => (
|
||||||
|
<tr key={user.id} className="border-b border-slate-200 hover:bg-slate-50 transition-colors">
|
||||||
|
<td className="px-6 py-4 text-slate-900 font-medium">{user.name}</td>
|
||||||
|
<td className="px-6 py-4 text-slate-600">{user.email}</td>
|
||||||
|
<td className="px-6 py-4 text-slate-600">
|
||||||
|
<span className="capitalize">{user.role.replace("_", " ")}</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 text-slate-600">{user.joinDate}</td>
|
||||||
|
<td className="px-6 py-4">
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||||
|
user.status === "active"
|
||||||
|
? "bg-green-100 text-green-800"
|
||||||
|
: "bg-gray-100 text-gray-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{user.status.charAt(0).toUpperCase() + user.status.slice(1)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 flex gap-2">
|
||||||
|
<button className="p-2 text-blue-600 hover:bg-blue-50 rounded transition-colors">
|
||||||
|
<Edit2 size={18} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => handleDeleteUser(user.id)}
|
||||||
|
className="p-2 text-red-600 hover:bg-red-50 rounded transition-colors"
|
||||||
|
>
|
||||||
|
<Trash2 size={18} />
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="footer" data-section="footer">
|
<div id="footer" data-section="footer">
|
||||||
<FooterBase
|
<FooterBase
|
||||||
logoText="Jobee Admin"
|
logoText="Jobee"
|
||||||
copyrightText="© 2025 Jobee Admin Portal"
|
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
|
||||||
columns={footerColumns}
|
columns={footerColumns}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,43 +2,114 @@
|
|||||||
|
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
import MetricCardTen from "@/components/sections/metrics/MetricCardTen";
|
|
||||||
import FeatureBento from "@/components/sections/feature/FeatureBento";
|
|
||||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
import {
|
import { FileText, Clock, CheckCircle, XCircle, Mail } from "lucide-react";
|
||||||
Users,
|
import { useState } from "react";
|
||||||
BarChart3,
|
|
||||||
Shield,
|
|
||||||
Activity,
|
|
||||||
TrendingUp,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: "Dashboard", id: "/admin" },
|
{ name: "Search Jobs", id: "search" },
|
||||||
{ name: "Jobs", id: "/admin#jobs" },
|
{ name: "Post a Job", id: "/post-job" },
|
||||||
{ name: "Users", id: "/admin#users" },
|
{ name: "Applications", id: "/applications" },
|
||||||
{ name: "Moderation", id: "/admin#moderation" },
|
{ name: "Browse", id: "browse" },
|
||||||
{ name: "Analytics", id: "/admin#analytics" },
|
{ name: "Contact", id: "contact" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const footerColumns = [
|
const footerColumns = [
|
||||||
{
|
{
|
||||||
title: "Admin", items: [
|
title: "Product", items: [
|
||||||
{ label: "Dashboard", href: "/admin" },
|
{ label: "Search Jobs", href: "/search" },
|
||||||
{ label: "Settings", href: "/admin#settings" },
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
{ label: "Reports", href: "/admin#reports" },
|
{ label: "My Applications", href: "/applications" },
|
||||||
|
{ label: "For Employers", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Quick Links", items: [
|
title: "Company", items: [
|
||||||
{ label: "Help", href: "#" },
|
{ label: "About Jobee", href: "#about" },
|
||||||
|
{ label: "Careers", href: "#" },
|
||||||
|
{ label: "Contact Us", href: "#contact" },
|
||||||
|
{ label: "Blog", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources", items: [
|
||||||
|
{ label: "Privacy Policy", href: "#" },
|
||||||
|
{ label: "Terms of Service", href: "#" },
|
||||||
|
{ label: "FAQ", href: "#" },
|
||||||
{ label: "Support", href: "#" },
|
{ label: "Support", href: "#" },
|
||||||
{ label: "Documentation", href: "#" },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
interface Application {
|
||||||
|
id: string;
|
||||||
|
jobTitle: string;
|
||||||
|
company: string;
|
||||||
|
status: "pending" | "reviewing" | "accepted" | "rejected";
|
||||||
|
appliedDate: string;
|
||||||
|
lastUpdate: string;
|
||||||
|
applicantName: string;
|
||||||
|
email: string;
|
||||||
|
appliedPosition?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockApplications: Application[] = [
|
||||||
|
{
|
||||||
|
id: "1", jobTitle: "Senior Frontend Developer", company: "Tech Innovations BV", status: "reviewing", appliedDate: "2025-01-15", lastUpdate: "2025-01-18", applicantName: "John Doe", email: "john.doe@example.com"},
|
||||||
|
{
|
||||||
|
id: "2", jobTitle: "Product Manager", company: "Digital Solutions Inc", status: "pending", appliedDate: "2025-01-20", lastUpdate: "2025-01-20", applicantName: "Jane Smith", email: "jane.smith@example.com"},
|
||||||
|
{
|
||||||
|
id: "3", jobTitle: "UX/UI Designer", company: "Creative Studio Amsterdam", status: "accepted", appliedDate: "2025-01-10", lastUpdate: "2025-01-17", applicantName: "Alice Johnson", email: "alice.johnson@example.com"},
|
||||||
|
{
|
||||||
|
id: "4", jobTitle: "Data Scientist", company: "AI Labs Netherlands", status: "rejected", appliedDate: "2025-01-05", lastUpdate: "2025-01-16", applicantName: "Bob Wilson", email: "bob.wilson@example.com"},
|
||||||
|
{
|
||||||
|
id: "5", jobTitle: "Backend Developer", company: "Cloud Systems Ltd", status: "reviewing", appliedDate: "2025-01-12", lastUpdate: "2025-01-19", applicantName: "Charlie Brown", email: "charlie.brown@example.com"},
|
||||||
|
];
|
||||||
|
|
||||||
export default function ApplicationsPage() {
|
export default function ApplicationsPage() {
|
||||||
|
const [applications, setApplications] = useState<Application[]>(mockApplications);
|
||||||
|
const [filterStatus, setFilterStatus] = useState<string>("all");
|
||||||
|
const [selectedApp, setSelectedApp] = useState<Application | null>(null);
|
||||||
|
|
||||||
|
const filteredApplications =
|
||||||
|
filterStatus === "all"
|
||||||
|
? applications
|
||||||
|
: applications.filter((app) => app.status === filterStatus);
|
||||||
|
|
||||||
|
const getStatusIcon = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case "pending":
|
||||||
|
return <Clock className="w-5 h-5 text-yellow-600" />;
|
||||||
|
case "reviewing":
|
||||||
|
return <FileText className="w-5 h-5 text-blue-600" />;
|
||||||
|
case "accepted":
|
||||||
|
return <CheckCircle className="w-5 h-5 text-green-600" />;
|
||||||
|
case "rejected":
|
||||||
|
return <XCircle className="w-5 h-5 text-red-600" />;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusBadgeColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case "pending":
|
||||||
|
return "bg-yellow-100 text-yellow-800";
|
||||||
|
case "reviewing":
|
||||||
|
return "bg-blue-100 text-blue-800";
|
||||||
|
case "accepted":
|
||||||
|
return "bg-green-100 text-green-800";
|
||||||
|
case "rejected":
|
||||||
|
return "bg-red-100 text-red-800";
|
||||||
|
default:
|
||||||
|
return "bg-slate-100 text-slate-800";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusLabel = (status: string) => {
|
||||||
|
return status.charAt(0).toUpperCase() + status.slice(1);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="text-stagger"
|
defaultButtonVariant="text-stagger"
|
||||||
@@ -54,67 +125,171 @@ export default function ApplicationsPage() {
|
|||||||
>
|
>
|
||||||
<div id="nav" data-section="nav">
|
<div id="nav" data-section="nav">
|
||||||
<NavbarStyleCentered
|
<NavbarStyleCentered
|
||||||
brandName="Jobee Admin"
|
brandName="Jobee"
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
button={{
|
button={{
|
||||||
text: "Logout", onClick: () => console.log("logout"),
|
text: "Post a Job", href: "/post-job"}}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="applications" data-section="applications" className="pt-20">
|
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 pt-32 pb-20">
|
||||||
<MetricCardTen
|
<div className="max-w-6xl mx-auto px-4">
|
||||||
title="Job Management"
|
<div className="mb-8">
|
||||||
description="Monitor and manage all job postings across the platform"
|
<div className="flex items-center gap-3 mb-4">
|
||||||
tag="Active Jobs"
|
<Mail className="w-6 h-6 text-blue-600" />
|
||||||
textboxLayout="default"
|
<span className="text-sm font-semibold text-blue-600 uppercase tracking-wide">
|
||||||
useInvertedBackground={false}
|
My Applications
|
||||||
animationType="slide-up"
|
</span>
|
||||||
metrics={[
|
</div>
|
||||||
{
|
<h1 className="text-4xl md:text-5xl font-bold text-slate-900 mb-3">
|
||||||
id: "1", title: "Senior Software Engineer, Backend", subtitle: "Amsterdam, Netherlands · Full-time · Remote eligible", category: "Engineering", value: "Posted 2 days ago", buttons: [
|
Track Your Applications
|
||||||
{ text: "View", href: "#" },
|
</h1>
|
||||||
{ text: "Manage", href: "#" },
|
<p className="text-lg text-slate-600">
|
||||||
],
|
Monitor the status of all your job applications and stay updated on each
|
||||||
},
|
opportunity.
|
||||||
{
|
</p>
|
||||||
id: "2", title: "Product Manager, Enterprise", subtitle: "Rotterdam, Netherlands · Full-time", category: "Product", value: "Posted 5 days ago", buttons: [
|
</div>
|
||||||
{ text: "View", href: "#" },
|
|
||||||
{ text: "Manage", href: "#" },
|
{/* Filter Tabs */}
|
||||||
],
|
<div className="mb-8 flex flex-wrap gap-3">
|
||||||
},
|
{[
|
||||||
{
|
{ label: "All", value: "all", count: applications.length },
|
||||||
id: "3", title: "UX Designer, B2B", subtitle: "Utrecht, Netherlands · Full-time", category: "Design", value: "Posted 1 week ago", buttons: [
|
{
|
||||||
{ text: "View", href: "#" },
|
label: "Pending", value: "pending", count: applications.filter((a) => a.status === "pending").length,
|
||||||
{ text: "Manage", href: "#" },
|
},
|
||||||
],
|
{
|
||||||
},
|
label: "Reviewing", value: "reviewing", count: applications.filter((a) => a.status === "reviewing").length,
|
||||||
{
|
},
|
||||||
id: "4", title: "Data Scientist, ML Platform", subtitle: "Remote · Full-time", category: "Data", value: "Posted 10 days ago", buttons: [
|
{
|
||||||
{ text: "View", href: "#" },
|
label: "Accepted", value: "accepted", count: applications.filter((a) => a.status === "accepted").length,
|
||||||
{ text: "Manage", href: "#" },
|
},
|
||||||
],
|
{
|
||||||
},
|
label: "Rejected", value: "rejected", count: applications.filter((a) => a.status === "rejected").length,
|
||||||
{
|
},
|
||||||
id: "5", title: "DevOps Engineer, Infrastructure", subtitle: "Amsterdam, Netherlands · Full-time · Remote", category: "Infrastructure", value: "Posted 3 weeks ago", buttons: [
|
].map((filter) => (
|
||||||
{ text: "View", href: "#" },
|
<button
|
||||||
{ text: "Manage", href: "#" },
|
key={filter.value}
|
||||||
],
|
onClick={() => setFilterStatus(filter.value)}
|
||||||
},
|
className={`px-4 py-2 rounded-lg font-semibold transition ${
|
||||||
{
|
filterStatus === filter.value
|
||||||
id: "6", title: "Marketing Manager, Growth", subtitle: "The Hague, Netherlands · Full-time", category: "Marketing", value: "Posted 1 month ago", buttons: [
|
? "bg-blue-600 text-white"
|
||||||
{ text: "View", href: "#" },
|
: "bg-white text-slate-700 hover:bg-slate-100"
|
||||||
{ text: "Manage", href: "#" },
|
}`}
|
||||||
],
|
>
|
||||||
},
|
{filter.label} ({filter.count})
|
||||||
]}
|
</button>
|
||||||
/>
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Applications List */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
{filteredApplications.length === 0 ? (
|
||||||
|
<div className="text-center py-12 bg-white rounded-lg">
|
||||||
|
<p className="text-slate-600 text-lg">No applications found</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
filteredApplications.map((app) => (
|
||||||
|
<div
|
||||||
|
key={app.id}
|
||||||
|
className="bg-white rounded-lg shadow hover:shadow-md transition p-6 cursor-pointer"
|
||||||
|
onClick={() => setSelectedApp(app)}
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-3 mb-2">
|
||||||
|
<h3 className="text-xl font-bold text-slate-900">{app.jobTitle}</h3>
|
||||||
|
<div
|
||||||
|
className={`flex items-center gap-1 px-3 py-1 rounded-full text-sm font-semibold ${
|
||||||
|
getStatusBadgeColor(app.status)
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{getStatusIcon(app.status)}
|
||||||
|
{getStatusLabel(app.status)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p className="text-slate-600 mb-2">{app.company}</p>
|
||||||
|
<div className="text-sm text-slate-500 space-y-1">
|
||||||
|
<p>Applied: {new Date(app.appliedDate).toLocaleDateString()}</p>
|
||||||
|
<p>Last Update: {new Date(app.lastUpdate).toLocaleDateString()}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Detail Modal */}
|
||||||
|
{selectedApp && (
|
||||||
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
||||||
|
<div className="bg-white rounded-lg max-w-2xl w-full max-h-96 overflow-y-auto">
|
||||||
|
<div className="p-8">
|
||||||
|
<div className="flex items-start justify-between mb-6">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-2xl font-bold text-slate-900 mb-2">
|
||||||
|
{selectedApp.jobTitle}
|
||||||
|
</h2>
|
||||||
|
<p className="text-slate-600 text-lg">{selectedApp.company}</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`flex items-center gap-2 px-4 py-2 rounded-full font-semibold ${
|
||||||
|
getStatusBadgeColor(selectedApp.status)
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{getStatusIcon(selectedApp.status)}
|
||||||
|
{getStatusLabel(selectedApp.status)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t pt-6 space-y-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-slate-600">Applicant Name</p>
|
||||||
|
<p className="text-lg font-semibold text-slate-900">{selectedApp.applicantName}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-slate-600">Email Address</p>
|
||||||
|
<p className="text-lg font-semibold text-slate-900">{selectedApp.email}</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-slate-600">Applied Date</p>
|
||||||
|
<p className="text-lg font-semibold text-slate-900">
|
||||||
|
{new Date(selectedApp.appliedDate).toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-slate-600">Last Update</p>
|
||||||
|
<p className="text-lg font-semibold text-slate-900">
|
||||||
|
{new Date(selectedApp.lastUpdate).toLocaleDateString()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t mt-6 pt-6">
|
||||||
|
<div className="flex gap-3">
|
||||||
|
<button
|
||||||
|
onClick={() => setSelectedApp(null)}
|
||||||
|
className="flex-1 bg-slate-200 hover:bg-slate-300 text-slate-900 font-semibold py-2 rounded-lg transition"
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
<button className="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 rounded-lg transition">
|
||||||
|
View Full Details
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="footer" data-section="footer">
|
<div id="footer" data-section="footer">
|
||||||
<FooterBase
|
<FooterBase
|
||||||
logoText="Jobee Admin"
|
logoText="Jobee"
|
||||||
copyrightText="© 2025 Jobee Admin Portal"
|
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
|
||||||
columns={footerColumns}
|
columns={footerColumns}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,70 +2,136 @@
|
|||||||
|
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
|
import FeatureCardEight from "@/components/sections/feature/FeatureCardEight";
|
||||||
|
import TestimonialCardTwo from "@/components/sections/testimonial/TestimonialCardTwo";
|
||||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
|
import { Briefcase, Sparkles } from "lucide-react";
|
||||||
const navItems = [
|
|
||||||
{ name: "Dashboard", id: "/" },
|
|
||||||
{ name: "Browse Jobs", id: "/search" },
|
|
||||||
{ name: "My Applications", id: "/applications" },
|
|
||||||
{ name: "Settings", id: "#settings" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const footerColumns = [
|
|
||||||
{
|
|
||||||
title: "Product", items: [
|
|
||||||
{ label: "Browse Jobs", href: "/search" },
|
|
||||||
{ label: "Companies", href: "#" },
|
|
||||||
{ label: "For Employers", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Company", items: [
|
|
||||||
{ label: "About", href: "#" },
|
|
||||||
{ label: "Blog", href: "#" },
|
|
||||||
{ label: "Contact", href: "#" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function ApplyPage() {
|
export default function ApplyPage() {
|
||||||
|
const navItems = [
|
||||||
|
{ name: "Search Jobs", id: "search" },
|
||||||
|
{ name: "Post a Job", id: "post-job" },
|
||||||
|
{ name: "Admin", id: "admin-login" },
|
||||||
|
{ name: "Browse", id: "browse" },
|
||||||
|
{ name: "Contact", id: "contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const footerColumns = [
|
||||||
|
{
|
||||||
|
title: "Product", items: [
|
||||||
|
{ label: "Search Jobs", href: "/search" },
|
||||||
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
|
{ label: "Browse by Province", href: "#provinces" },
|
||||||
|
{ label: "For Employers", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Company", items: [
|
||||||
|
{ label: "About Jobee", href: "#about" },
|
||||||
|
{ label: "Careers", href: "#" },
|
||||||
|
{ label: "Contact Us", href: "#contact" },
|
||||||
|
{ label: "Blog", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources", items: [
|
||||||
|
{ label: "Privacy Policy", href: "#" },
|
||||||
|
{ label: "Terms of Service", href: "#" },
|
||||||
|
{ label: "FAQ", href: "#" },
|
||||||
|
{ label: "Support", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="text-stagger"
|
defaultButtonVariant="text-stagger"
|
||||||
defaultTextAnimation="reveal-blur"
|
defaultTextAnimation="reveal-blur"
|
||||||
borderRadius="pill"
|
borderRadius="pill"
|
||||||
contentWidth="medium"
|
contentWidth="smallMedium"
|
||||||
sizing="medium"
|
sizing="mediumLargeSizeLargeTitles"
|
||||||
background="circleGradient"
|
background="circleGradient"
|
||||||
cardStyle="glass-elevated"
|
cardStyle="gradient-radial"
|
||||||
primaryButtonStyle="gradient"
|
primaryButtonStyle="double-inset"
|
||||||
secondaryButtonStyle="glass"
|
secondaryButtonStyle="glass"
|
||||||
headingFontWeight="normal"
|
headingFontWeight="bold"
|
||||||
>
|
>
|
||||||
<div id="nav" data-section="nav">
|
<div id="nav" data-section="nav">
|
||||||
<NavbarStyleCentered
|
<NavbarStyleCentered
|
||||||
brandName="Jobee"
|
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
button={{
|
button={{ text: "Post a Job", href: "/post-job" }}
|
||||||
text: "Sign In", onClick: () => console.log("sign-in"),
|
brandName="Jobee"
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="apply" data-section="apply" className="py-20">
|
<div id="features" data-section="features">
|
||||||
<div className="container mx-auto px-4 text-center">
|
<FeatureCardEight
|
||||||
<h1 className="text-4xl font-bold mb-4">Job Application Form</h1>
|
features={[
|
||||||
<p className="text-lg text-gray-600">Complete your profile to apply for this position</p>
|
{
|
||||||
</div>
|
id: 1,
|
||||||
|
title: "Complete Your Profile", description:
|
||||||
|
"Upload your resume, add a professional photo, and complete your profile information. Make a great first impression with employers.", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=5", imageAlt: "Profile completion step"},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Submit Your Application", description:
|
||||||
|
"Choose your desired position and submit your application with a personalized cover letter. It takes just minutes.", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=5", imageAlt: "Application submission process"},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Track and Communicate", description:
|
||||||
|
"Monitor your application status in real-time and communicate directly with employers through our messaging system.", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=6", imageAlt: "Application tracking dashboard"},
|
||||||
|
]}
|
||||||
|
title="Apply to Jobs in Three Steps"
|
||||||
|
description="Streamlined application process designed to help you land your dream job in the Netherlands."
|
||||||
|
tag="Easy Application"
|
||||||
|
tagIcon={Sparkles}
|
||||||
|
tagAnimation="slide-up"
|
||||||
|
textboxLayout="default"
|
||||||
|
useInvertedBackground={false}
|
||||||
|
buttons={[{ text: "Find Jobs Now", href: "/search" }]}
|
||||||
|
buttonAnimation="slide-up"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="testimonials" data-section="testimonials">
|
||||||
|
<TestimonialCardTwo
|
||||||
|
testimonials={[
|
||||||
|
{
|
||||||
|
id: "1", name: "Sarah van der Berg", role: "Software Developer", testimonial:
|
||||||
|
"I found my perfect job within 2 weeks using Jobee. The search filters made it easy to find remote opportunities in Amsterdam that matched my skill set.", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-photo/front-view-smiley-man-posing_23-2150171293.jpg?_wi=2", imageAlt: "Sarah van der Berg"},
|
||||||
|
{
|
||||||
|
id: "2", name: "Emma Dijkstra", role: "Marketing Specialist", testimonial:
|
||||||
|
"The application process on Jobee is so smooth. I applied for 5 positions and received 3 interview invitations within a week. Highly recommended!", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-photo/smiling-face-gorgeous-latin-american-woman_1262-5766.jpg?_wi=2", imageAlt: "Emma Dijkstra"},
|
||||||
|
{
|
||||||
|
id: "3", name: "Michael Houtstra", role: "Finance Director", testimonial:
|
||||||
|
"The platform's transparency about job requirements and company culture helped me find a role that was truly the right fit. Great experience!", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-photo/smiling-senior-businessman-pointing-with-finger_1262-3108.jpg?_wi=2", imageAlt: "Michael Houtstra"},
|
||||||
|
{
|
||||||
|
id: "4", name: "Lisa Bertrand", role: "UX Designer", testimonial:
|
||||||
|
"Jobee's application tracking feature is excellent. I always knew exactly where I stood with each employer. Best job search experience yet!", imageSrc:
|
||||||
|
"http://img.b2bpic.net/free-photo/studio-portrait-elegant-black-american-male-dressed-suit-grey-vignette-background_613910-9543.jpg?_wi=2", imageAlt: "Lisa Bertrand"},
|
||||||
|
]}
|
||||||
|
title="Success Stories from Our Job Seekers"
|
||||||
|
description="Real experiences from candidates who found their dream jobs through Jobee."
|
||||||
|
tag="User Testimonials"
|
||||||
|
tagAnimation="slide-up"
|
||||||
|
textboxLayout="default"
|
||||||
|
useInvertedBackground={true}
|
||||||
|
animationType="slide-up"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="footer" data-section="footer">
|
<div id="footer" data-section="footer">
|
||||||
<FooterBase
|
<FooterBase
|
||||||
logoText="Jobee"
|
|
||||||
copyrightText="© 2025 Jobee"
|
|
||||||
columns={footerColumns}
|
columns={footerColumns}
|
||||||
|
logoText="Jobee"
|
||||||
|
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
380
src/app/jobs/page.tsx
Normal file
380
src/app/jobs/page.tsx
Normal file
@@ -0,0 +1,380 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
|
import { MapPin, DollarSign, Briefcase, Heart } from "lucide-react";
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ name: "Search Jobs", id: "/search" },
|
||||||
|
{ name: "Post a Job", id: "/post-job" },
|
||||||
|
{ name: "Admin", id: "/admin-login" },
|
||||||
|
{ name: "Browse", id: "browse" },
|
||||||
|
{ name: "Contact", id: "contact" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const footerColumns = [
|
||||||
|
{
|
||||||
|
title: "Product", items: [
|
||||||
|
{ label: "Search Jobs", href: "/search" },
|
||||||
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
|
{ label: "Browse by Province", href: "#provinces" },
|
||||||
|
{ label: "For Employers", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Company", items: [
|
||||||
|
{ label: "About Jobee", href: "#about" },
|
||||||
|
{ label: "Careers", href: "#" },
|
||||||
|
{ label: "Contact Us", href: "#contact" },
|
||||||
|
{ label: "Blog", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources", items: [
|
||||||
|
{ label: "Privacy Policy", href: "#" },
|
||||||
|
{ label: "Terms of Service", href: "#" },
|
||||||
|
{ label: "FAQ", href: "#" },
|
||||||
|
{ label: "Support", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockJobs = [
|
||||||
|
{
|
||||||
|
id: "1", title: "Senior Software Engineer", company: "TechCorp Amsterdam", location: "Amsterdam", province: "North Holland", salary: "€80,000 - €120,000", salaryMin: 80000,
|
||||||
|
salaryMax: 120000,
|
||||||
|
jobType: "Full-time", category: "Technology", description:
|
||||||
|
"Join our innovative team as a Senior Software Engineer. We\'re looking for experienced developers with expertise in React, Node.js, and cloud technologies.", fullDescription:
|
||||||
|
"Join our innovative team as a Senior Software Engineer. We\'re looking for experienced developers with expertise in React, Node.js, and cloud technologies. This role offers the opportunity to work on cutting-edge projects, mentor junior developers, and contribute to architectural decisions.", requirements: [
|
||||||
|
"5+ years of software development experience", "Proficiency in React, Node.js, and TypeScript", "Experience with cloud platforms (AWS, GCP, Azure)", "Strong understanding of software design patterns", "Excellent communication skills"],
|
||||||
|
benefits: [
|
||||||
|
"Competitive salary and bonus structure", "Health insurance and wellness programs", "Flexible working hours and remote work options", "Professional development opportunities", "Modern office in Amsterdam city center"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/tech-company-logo_23-2148947635.jpg", postedDate: "2 days ago"},
|
||||||
|
{
|
||||||
|
id: "2", title: "Marketing Manager", company: "Creative Solutions Rotterdam", location: "Rotterdam", province: "South Holland", salary: "€60,000 - €85,000", salaryMin: 60000,
|
||||||
|
salaryMax: 85000,
|
||||||
|
jobType: "Full-time", category: "Marketing", description:
|
||||||
|
"Lead our marketing initiatives and drive brand growth. We seek a strategic marketer with experience in digital marketing and campaign management.", fullDescription:
|
||||||
|
"Lead our marketing initiatives and drive brand growth. We seek a strategic marketer with experience in digital marketing and campaign management. You\'ll oversee a team of marketing professionals and develop comprehensive marketing strategies to achieve business objectives.", requirements: [
|
||||||
|
"7+ years of marketing management experience", "Proven track record in digital marketing campaigns", "Strong analytical and data-driven decision making", "Leadership and team management experience", "Proficiency in marketing automation tools"],
|
||||||
|
benefits: [
|
||||||
|
"Competitive salary package", "Team leadership opportunities", "Conference and training budget", "Flexible working arrangement", "Performance-based bonuses"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/marketing-logo_23-2148947635.jpg", postedDate: "5 days ago"},
|
||||||
|
{
|
||||||
|
id: "3", title: "Data Scientist", company: "Analytics Pro Utrecht", location: "Utrecht", province: "Utrecht", salary: "€70,000 - €110,000", salaryMin: 70000,
|
||||||
|
salaryMax: 110000,
|
||||||
|
jobType: "Full-time", category: "Data Science", description:
|
||||||
|
"Develop advanced analytics solutions for our global clients. Expertise in Python, machine learning, and big data technologies required.", fullDescription:
|
||||||
|
"Develop advanced analytics solutions for our global clients. Expertise in Python, machine learning, and big data technologies required. Work on complex data challenges, build predictive models, and transform raw data into actionable insights.", requirements: [
|
||||||
|
"Advanced degree in Computer Science, Mathematics, or related field", "Proficiency in Python and machine learning libraries", "Experience with big data technologies (Spark, Hadoop)", "Strong SQL and database knowledge", "Experience with data visualization tools"],
|
||||||
|
benefits: [
|
||||||
|
"Excellent salary and benefits", "State-of-the-art computing resources", "Continuous learning opportunities", "Collaborative research environment", "Publication opportunities"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/analytics-logo_23-2148947635.jpg", postedDate: "1 week ago"},
|
||||||
|
{
|
||||||
|
id: "4", title: "UX/UI Designer", company: "Design Studios The Hague", location: "The Hague", province: "South Holland", salary: "€55,000 - €75,000", salaryMin: 55000,
|
||||||
|
salaryMax: 75000,
|
||||||
|
jobType: "Full-time", category: "Design", description:
|
||||||
|
"Create beautiful and intuitive user experiences for our web and mobile applications. Portfolio required.", fullDescription:
|
||||||
|
"Create beautiful and intuitive user experiences for our web and mobile applications. Portfolio required. Collaborate with product teams, conduct user research, and iterate on designs based on user feedback and analytics.", requirements: [
|
||||||
|
"4+ years of UX/UI design experience", "Proficiency in design tools (Figma, Adobe XD)", "Strong portfolio showcasing design work", "Understanding of user research and testing methodologies", "Knowledge of responsive design principles"],
|
||||||
|
benefits: [
|
||||||
|
"Creative and collaborative work environment", "Access to latest design tools and technologies", "Design conference attendance budget", "Flexible work schedule", "Health and wellness benefits"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/design-logo_23-2148947635.jpg", postedDate: "3 days ago"},
|
||||||
|
{
|
||||||
|
id: "5", title: "Sales Executive", company: "Enterprise Solutions Groningen", location: "Groningen", province: "Groningen", salary: "€45,000 - €70,000", salaryMin: 45000,
|
||||||
|
salaryMax: 70000,
|
||||||
|
jobType: "Full-time", category: "Sales", description:
|
||||||
|
"Grow our sales pipeline and build strong client relationships. Commission and bonus structure available.", fullDescription:
|
||||||
|
"Grow our sales pipeline and build strong client relationships. Commission and bonus structure available. Manage a portfolio of accounts, identify new business opportunities, and achieve sales targets.", requirements: [
|
||||||
|
"3+ years of B2B sales experience", "Strong negotiation and closing skills", "CRM software proficiency", "Excellent communication abilities", "Self-motivated and target-driven"],
|
||||||
|
benefits: [
|
||||||
|
"Competitive base salary plus commission", "Performance bonuses", "Sales training and development", "Company car or allowance", "International travel opportunities"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/sales-logo_23-2148947635.jpg", postedDate: "4 days ago"},
|
||||||
|
{
|
||||||
|
id: "6", title: "HR Specialist", company: "People First Leiden", location: "Leiden", province: "South Holland", salary: "€50,000 - €65,000", salaryMin: 50000,
|
||||||
|
salaryMax: 65000,
|
||||||
|
jobType: "Part-time", category: "Human Resources", description:
|
||||||
|
"Support our HR team in recruitment, onboarding, and employee development initiatives.", fullDescription:
|
||||||
|
"Support our HR team in recruitment, onboarding, and employee development initiatives. Handle administrative HR tasks, coordinate interviews, and support employee relations.", requirements: [
|
||||||
|
"3+ years of HR experience", "Knowledge of Dutch employment law", "Proficiency in HR management systems", "Strong organizational skills", "Excellent interpersonal abilities"],
|
||||||
|
benefits: [
|
||||||
|
"Competitive part-time salary", "Flexible working hours", "Professional HR certifications support", "Health insurance", "Staff development programs"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/hr-logo_23-2148947635.jpg", postedDate: "1 week ago"},
|
||||||
|
{
|
||||||
|
id: "7", title: "DevOps Engineer", company: "Cloud Innovations Eindhoven", location: "Eindhoven", province: "North Brabant", salary: "€75,000 - €105,000", salaryMin: 75000,
|
||||||
|
salaryMax: 105000,
|
||||||
|
jobType: "Full-time", category: "Technology", description:
|
||||||
|
"Manage and optimize our cloud infrastructure. Experience with Docker, Kubernetes, and CI/CD pipelines required.", fullDescription:
|
||||||
|
"Manage and optimize our cloud infrastructure. Experience with Docker, Kubernetes, and CI/CD pipelines required. Build and maintain deployment pipelines, monitor system performance, and implement security best practices.", requirements: [
|
||||||
|
"5+ years of DevOps experience", "Expert knowledge of Docker and Kubernetes", "CI/CD pipeline implementation experience", "Strong Linux administration skills", "Cloud platform experience (AWS/GCP/Azure)"],
|
||||||
|
benefits: [
|
||||||
|
"High competitive salary", "Remote work options", "Technical certification support", "Modern tech stack", "Collaborative engineering team"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/devops-logo_23-2148947635.jpg", postedDate: "6 days ago"},
|
||||||
|
{
|
||||||
|
id: "8", title: "Product Manager", company: "Innovation Lab Delft", location: "Delft", province: "South Holland", salary: "€70,000 - €95,000", salaryMin: 70000,
|
||||||
|
salaryMax: 95000,
|
||||||
|
jobType: "Full-time", category: "Product", description:
|
||||||
|
"Lead product strategy and roadmap development for our SaaS platform. Experience with agile methodologies required.", fullDescription:
|
||||||
|
"Lead product strategy and roadmap development for our SaaS platform. Experience with agile methodologies required. Work cross-functionally with engineering, design, and marketing teams to deliver customer-centric solutions.", requirements: [
|
||||||
|
"5+ years of product management experience", "Expertise in SaaS business models", "Proficiency in product management tools", "Strong analytical skills", "Experience with agile and Scrum methodologies"],
|
||||||
|
benefits: [
|
||||||
|
"Attractive salary and equity", "Leadership development programs", "Industry conference attendance", "Flexible work arrangement", "Collaborative innovation environment"],
|
||||||
|
logo: "http://img.b2bpic.net/free-vector/product-logo_23-2148947635.jpg", postedDate: "2 days ago"},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface JobCardProps {
|
||||||
|
job: (typeof mockJobs)[0];
|
||||||
|
isFavorited: boolean;
|
||||||
|
onFavorite: (id: string) => void;
|
||||||
|
onClick: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function JobCard({ job, isFavorited, onFavorite, onClick }: JobCardProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
onClick={onClick}
|
||||||
|
className="p-6 rounded-xl bg-card border border-accent/20 hover:border-accent/50 transition-all duration-300 hover:shadow-lg cursor-pointer group"
|
||||||
|
>
|
||||||
|
<div className="flex items-start justify-between mb-4">
|
||||||
|
<div className="flex items-start gap-4 flex-1">
|
||||||
|
<div className="w-16 h-16 rounded-lg bg-gradient-to-br from-primary-cta to-secondary-cta flex items-center justify-center text-white font-bold text-xl flex-shrink-0 group-hover:shadow-lg transition-shadow">
|
||||||
|
{job.company.charAt(0)}
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-xl font-bold text-foreground mb-1 group-hover:text-primary-cta transition-colors">
|
||||||
|
{job.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-foreground/70 mb-2">{job.company}</p>
|
||||||
|
<div className="flex items-center gap-4 text-sm text-foreground/60">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<MapPin size={16} />
|
||||||
|
{job.location}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Briefcase size={16} />
|
||||||
|
{job.jobType}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onFavorite(job.id);
|
||||||
|
}}
|
||||||
|
className="p-2 rounded-full hover:bg-accent/10 transition-colors flex-shrink-0"
|
||||||
|
>
|
||||||
|
<Heart
|
||||||
|
size={20}
|
||||||
|
className={isFavorited ? "fill-primary-cta text-primary-cta" : "text-foreground/50"}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-foreground/70 mb-4 line-clamp-2">
|
||||||
|
{job.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2 items-center">
|
||||||
|
<span className="inline-block px-3 py-1 rounded-lg text-xs bg-background text-foreground/70">
|
||||||
|
{job.category}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-semibold text-primary-cta flex items-center gap-1">
|
||||||
|
<DollarSign size={14} />
|
||||||
|
{job.salary}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button className="px-4 py-2 rounded-full bg-primary-cta text-white font-semibold hover:opacity-90 transition-opacity text-sm">
|
||||||
|
Apply Now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function JobListingPage() {
|
||||||
|
const [selectedJob, setSelectedJob] = useState<(typeof mockJobs)[0] | null>(null);
|
||||||
|
const [favorites, setFavorites] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
|
const handleFavorite = (jobId: string) => {
|
||||||
|
const newFavorites = new Set(favorites);
|
||||||
|
if (newFavorites.has(jobId)) {
|
||||||
|
newFavorites.delete(jobId);
|
||||||
|
} else {
|
||||||
|
newFavorites.add(jobId);
|
||||||
|
}
|
||||||
|
setFavorites(newFavorites);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ThemeProvider
|
||||||
|
defaultButtonVariant="text-stagger"
|
||||||
|
defaultTextAnimation="reveal-blur"
|
||||||
|
borderRadius="pill"
|
||||||
|
contentWidth="smallMedium"
|
||||||
|
sizing="mediumLargeSizeLargeTitles"
|
||||||
|
background="circleGradient"
|
||||||
|
cardStyle="gradient-radial"
|
||||||
|
primaryButtonStyle="double-inset"
|
||||||
|
secondaryButtonStyle="glass"
|
||||||
|
headingFontWeight="bold"
|
||||||
|
>
|
||||||
|
<div id="nav" data-section="nav">
|
||||||
|
<NavbarStyleCentered
|
||||||
|
brandName="Jobee"
|
||||||
|
navItems={navItems}
|
||||||
|
button={{
|
||||||
|
text: "Post a Job", href: "/post-job"}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="min-h-screen bg-gradient-to-b from-background to-background-accent pt-20 pb-20">
|
||||||
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||||
|
{/* Job Listings Column */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<div className="mb-8">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-2">
|
||||||
|
Latest Job Listings
|
||||||
|
</h1>
|
||||||
|
<p className="text-lg text-foreground/70">
|
||||||
|
{mockJobs.length} positions available
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
{mockJobs.map((job) => (
|
||||||
|
<JobCard
|
||||||
|
key={job.id}
|
||||||
|
job={job}
|
||||||
|
isFavorited={favorites.has(job.id)}
|
||||||
|
onFavorite={handleFavorite}
|
||||||
|
onClick={() => setSelectedJob(job)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Job Details Column */}
|
||||||
|
<div className="lg:col-span-1">
|
||||||
|
{selectedJob ? (
|
||||||
|
<div className="sticky top-28 p-6 rounded-xl bg-card border border-accent/20">
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="w-20 h-20 rounded-lg bg-gradient-to-br from-primary-cta to-secondary-cta flex items-center justify-center text-white font-bold text-2xl mb-4">
|
||||||
|
{selectedJob.company.charAt(0)}
|
||||||
|
</div>
|
||||||
|
<h2 className="text-2xl font-bold text-foreground mb-2">
|
||||||
|
{selectedJob.title}
|
||||||
|
</h2>
|
||||||
|
<p className="text-foreground/70 mb-4">{selectedJob.company}</p>
|
||||||
|
<p className="text-2xl font-bold text-primary-cta mb-4">
|
||||||
|
{selectedJob.salary}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
handleFavorite(selectedJob.id);
|
||||||
|
}}
|
||||||
|
className="w-full py-3 rounded-full bg-primary-cta/10 text-primary-cta font-semibold hover:bg-primary-cta/20 transition-colors flex items-center justify-center gap-2 mb-3"
|
||||||
|
>
|
||||||
|
<Heart
|
||||||
|
size={18}
|
||||||
|
className={favorites.has(selectedJob.id) ? "fill-primary-cta" : ""}
|
||||||
|
/>
|
||||||
|
{favorites.has(selectedJob.id) ? "Saved" : "Save Job"}
|
||||||
|
</button>
|
||||||
|
<button className="w-full py-3 rounded-full bg-primary-cta text-white font-semibold hover:opacity-90 transition-opacity">
|
||||||
|
Apply Now
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-accent/20 pt-6">
|
||||||
|
<h3 className="text-lg font-bold text-foreground mb-4">
|
||||||
|
Job Details
|
||||||
|
</h3>
|
||||||
|
<div className="space-y-3 text-sm">
|
||||||
|
<div>
|
||||||
|
<p className="text-foreground/60 mb-1">Posted</p>
|
||||||
|
<p className="text-foreground font-semibold">
|
||||||
|
{selectedJob.postedDate}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-foreground/60 mb-1">Location</p>
|
||||||
|
<p className="text-foreground font-semibold">
|
||||||
|
{selectedJob.location}, {selectedJob.province}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-foreground/60 mb-1">Job Type</p>
|
||||||
|
<p className="text-foreground font-semibold">
|
||||||
|
{selectedJob.jobType}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-foreground/60 mb-1">Category</p>
|
||||||
|
<p className="text-foreground font-semibold">
|
||||||
|
{selectedJob.category}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-accent/20 pt-6 mt-6">
|
||||||
|
<h3 className="text-lg font-bold text-foreground mb-4">
|
||||||
|
Requirements
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-2 text-sm">
|
||||||
|
{selectedJob.requirements.map((req, idx) => (
|
||||||
|
<li key={idx} className="flex gap-3 text-foreground/70">
|
||||||
|
<span className="text-primary-cta font-bold flex-shrink-0">
|
||||||
|
✓
|
||||||
|
</span>
|
||||||
|
<span>{req}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-accent/20 pt-6 mt-6">
|
||||||
|
<h3 className="text-lg font-bold text-foreground mb-4">
|
||||||
|
Benefits
|
||||||
|
</h3>
|
||||||
|
<ul className="space-y-2 text-sm">
|
||||||
|
{selectedJob.benefits.map((benefit, idx) => (
|
||||||
|
<li key={idx} className="flex gap-3 text-foreground/70">
|
||||||
|
<span className="text-primary-cta font-bold flex-shrink-0">
|
||||||
|
★
|
||||||
|
</span>
|
||||||
|
<span>{benefit}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="sticky top-28 p-6 rounded-xl bg-card border border-accent/20 border-dashed text-center">
|
||||||
|
<p className="text-foreground/60">
|
||||||
|
Select a job to view full details
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="footer" data-section="footer">
|
||||||
|
<FooterBase
|
||||||
|
logoText="Jobee"
|
||||||
|
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
|
||||||
|
columns={footerColumns}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,17 +1,11 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
const geist = Geist({
|
const inter = Inter({ variable: "--font-inter", subsets: ["latin"] });
|
||||||
variable: "--font-geist-sans", subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
|
||||||
variable: "--font-geist-mono", subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Jobee - Dutch Job Listings Platform", description: "Find your dream job in the Netherlands across all 12 provinces. Connect with top employers and build your career with Jobee."};
|
title: "Jobee - Dutch Job Listing Platform", description: "Find your dream job in the Netherlands across all 12 provinces. Connect with top employers and build your career."};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
@@ -20,16 +14,34 @@ export default function RootLayout({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body className={`${geist.variable} ${geistMono.variable}`}>
|
<body className={`${inter.variable}`}>
|
||||||
{children}
|
{children}
|
||||||
<script
|
<script
|
||||||
|
id="lenis-setup"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `
|
||||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
window.lenis = null;
|
||||||
document.documentElement.classList.add('dark')
|
if (typeof window !== 'undefined' && window.requestAnimationFrame) {
|
||||||
} else {
|
import('https://cdn.jsdelivr.net/npm/@studio-freight/lenis@1').then(({ default: Lenis }) => {
|
||||||
document.documentElement.classList.remove('dark')
|
if (window.lenis) return;
|
||||||
}
|
window.lenis = new Lenis({
|
||||||
|
duration: 1.2,
|
||||||
|
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
|
||||||
|
direction: 'vertical',
|
||||||
|
gestureDirection: 'vertical',
|
||||||
|
smooth: true,
|
||||||
|
mouseMultiplier: 1,
|
||||||
|
smoothTouch: false,
|
||||||
|
touchInertiaMultiplier: 2,
|
||||||
|
infinite: false,
|
||||||
|
});
|
||||||
|
function raf(time) {
|
||||||
|
if (window.lenis) window.lenis.raf(time);
|
||||||
|
requestAnimationFrame(raf);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(raf);
|
||||||
|
}).catch(err => console.log('Lenis load error', err));
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
175
src/app/page.tsx
175
src/app/page.tsx
@@ -2,139 +2,208 @@
|
|||||||
|
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
import HeroSplitDoubleCarousel from '@/components/sections/hero/HeroSplitDoubleCarousel';
|
import HeroSplitDoubleCarousel from "@/components/sections/hero/HeroSplitDoubleCarousel";
|
||||||
import FeatureCardEight from '@/components/sections/feature/FeatureCardEight';
|
import FeatureCardEight from "@/components/sections/feature/FeatureCardEight";
|
||||||
import TestimonialCardTwo from '@/components/sections/testimonial/TestimonialCardTwo';
|
import TestimonialCardTwo from "@/components/sections/testimonial/TestimonialCardTwo";
|
||||||
import ContactCenter from '@/components/sections/contact/ContactCenter';
|
import ContactCenter from "@/components/sections/contact/ContactCenter";
|
||||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
import { Users, Star } from "lucide-react";
|
import { Briefcase, Sparkles, Mail, Quote } from "lucide-react";
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: "Home", id: "/" },
|
{ name: "Search Jobs", id: "search" },
|
||||||
{ name: "Features", id: "#features" },
|
{ name: "Post a Job", id: "post-job" },
|
||||||
{ name: "Testimonials", id: "#testimonials" },
|
{ name: "Admin", id: "/admin" },
|
||||||
{ name: "Contact", id: "#contact" },
|
{ name: "Browse", id: "browse" },
|
||||||
|
{ name: "Contact", id: "contact" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const footerColumns = [
|
const footerColumns = [
|
||||||
{
|
{
|
||||||
title: "Product", items: [
|
title: "Product", items: [
|
||||||
{ label: "Features", href: "#features" },
|
{ label: "Search Jobs", href: "/search" },
|
||||||
{ label: "Pricing", href: "#pricing" },
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
{ label: "Security", href: "#security" },
|
{ label: "Browse by Province", href: "#provinces" },
|
||||||
|
{ label: "For Employers", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Company", items: [
|
title: "Company", items: [
|
||||||
{ label: "About", href: "#about" },
|
{ label: "About Jobee", href: "#about" },
|
||||||
{ label: "Blog", href: "#blog" },
|
{ label: "Careers", href: "#" },
|
||||||
{ label: "Careers", href: "#careers" },
|
{ label: "Contact Us", href: "#contact" },
|
||||||
|
{ label: "Blog", href: "#" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources", items: [
|
||||||
|
{ label: "Privacy Policy", href: "#" },
|
||||||
|
{ label: "Terms of Service", href: "#" },
|
||||||
|
{ label: "FAQ", href: "#" },
|
||||||
|
{ label: "Support", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Home() {
|
export default function HomePage() {
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="text-stagger"
|
defaultButtonVariant="text-stagger"
|
||||||
defaultTextAnimation="entrance-slide"
|
defaultTextAnimation="reveal-blur"
|
||||||
borderRadius="rounded"
|
borderRadius="pill"
|
||||||
contentWidth="medium"
|
contentWidth="smallMedium"
|
||||||
sizing="medium"
|
sizing="mediumLargeSizeLargeTitles"
|
||||||
background="circleGradient"
|
background="circleGradient"
|
||||||
cardStyle="glass-elevated"
|
cardStyle="gradient-radial"
|
||||||
primaryButtonStyle="gradient"
|
primaryButtonStyle="double-inset"
|
||||||
secondaryButtonStyle="glass"
|
secondaryButtonStyle="glass"
|
||||||
headingFontWeight="normal"
|
headingFontWeight="bold"
|
||||||
>
|
>
|
||||||
<div id="nav" data-section="nav">
|
<div id="nav" data-section="nav">
|
||||||
<NavbarStyleCentered
|
<NavbarStyleCentered
|
||||||
brandName="Webild"
|
brandName="Jobee"
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
button={{
|
button={{
|
||||||
text: "Get Started", onClick: () => console.log("cta clicked"),
|
text: "Post a Job", href: "/post-job"
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="hero" data-section="hero">
|
<div id="hero" data-section="hero">
|
||||||
<HeroSplitDoubleCarousel
|
<HeroSplitDoubleCarousel
|
||||||
title="Experience the Future of Web Development"
|
title="Find Your Dream Job in the Netherlands"
|
||||||
description="Build stunning, responsive websites with our revolutionary platform and innovative tools."
|
description="Discover thousands of opportunities across all 12 Dutch provinces. Connect with top employers, build your career, and find the perfect role that matches your skills and ambitions."
|
||||||
background={{ variant: "sparkles-gradient" }}
|
tag="Dutch Job Market"
|
||||||
|
tagIcon={Briefcase}
|
||||||
|
tagAnimation="slide-up"
|
||||||
|
background={{
|
||||||
|
variant: "animated-grid"
|
||||||
|
}}
|
||||||
leftCarouselItems={[
|
leftCarouselItems={[
|
||||||
{ imageSrc: "/placeholders/placeholder1.webp", imageAlt: "Feature 1" },
|
{
|
||||||
{ imageSrc: "/placeholders/placeholder2.webp", imageAlt: "Feature 2" },
|
imageSrc: "http://img.b2bpic.net/free-vector/professional-bookkeeping-postcard-template_23-2149341358.jpg?_wi=1", imageAlt: "Job Listing Card Design"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageSrc: "http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=1", imageAlt: "Advanced Search Filter Interface"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageSrc: "http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=1", imageAlt: "Admin Dashboard Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageSrc: "http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=1", imageAlt: "Application Process Flow"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
rightCarouselItems={[
|
rightCarouselItems={[
|
||||||
{ imageSrc: "/placeholders/placeholder3.webp", imageAlt: "Feature 3" },
|
{
|
||||||
{ imageSrc: "/placeholders/placeholder4.webp", imageAlt: "Feature 4" },
|
imageSrc: "http://img.b2bpic.net/free-photo/corporate-workers-brainstorming-together_23-2148804568.jpg?_wi=1", imageAlt: "Professional Job Search Workspace"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageSrc: "http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=2", imageAlt: "Recruitment Application Steps"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageSrc: "http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=2", imageAlt: "Recruitment Analytics Dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
imageSrc: "http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=2", imageAlt: "Job Search Filter Options"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
buttons={[
|
buttons={[
|
||||||
{ text: "Get Started", href: "#contact" },
|
{
|
||||||
{ text: "Learn More", href: "#features" },
|
text: "Start Searching", href: "/search"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Post a Job", href: "/post-job"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
|
buttonAnimation="slide-up"
|
||||||
|
carouselPosition="right"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="features" data-section="features">
|
<div id="features" data-section="features">
|
||||||
<FeatureCardEight
|
<FeatureCardEight
|
||||||
title="Powerful Features"
|
title="How Jobee Works"
|
||||||
description="Everything you need to build amazing web experiences"
|
description="A seamless journey from job discovery to successful employment placement across the Netherlands."
|
||||||
tag="Features"
|
tag="Simple Process"
|
||||||
|
tagIcon={Sparkles}
|
||||||
|
tagAnimation="slide-up"
|
||||||
textboxLayout="default"
|
textboxLayout="default"
|
||||||
useInvertedBackground={false}
|
useInvertedBackground={false}
|
||||||
features={[
|
features={[
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
title: "Lightning Fast", description: "Optimized for speed and performance", imageSrc: "/placeholders/placeholder1.webp", imageAlt: "Performance"},
|
title: "Search & Filter", description: "Browse thousands of jobs across all Dutch provinces with advanced filtering by location, salary, job type, and category.", imageSrc: "http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=3", imageAlt: "Advanced search interface"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
title: "Fully Responsive", description: "Perfect on all devices and screen sizes", imageSrc: "/placeholders/placeholder2.webp", imageAlt: "Responsive"},
|
title: "Apply Easily", description: "Submit your application with your resume, cover letter, and personal information in just minutes.", imageSrc: "http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=3", imageAlt: "Application form process"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
title: "Easy to Use", description: "Intuitive interface for seamless workflows", imageSrc: "/placeholders/placeholder3.webp", imageAlt: "Easy to use"},
|
title: "Connect with Employers", description: "Get matched with top Dutch companies actively hiring and receive direct opportunities from recruiters.", imageSrc: "http://img.b2bpic.net/free-photo/corporate-workers-brainstorming-together_23-2148804568.jpg?_wi=2", imageAlt: "Professional networking"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
title: "Secure by Default", description: "Enterprise-grade security built-in", imageSrc: "/placeholders/placeholder4.webp", imageAlt: "Secure"},
|
title: "Land Your Dream Job", description: "Track your applications, receive updates, and secure your next career opportunity with confidence.", imageSrc: "http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=3", imageAlt: "Dashboard tracking"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
|
buttons={[
|
||||||
|
{
|
||||||
|
text: "Browse Jobs", href: "/search"
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
buttonAnimation="slide-up"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="testimonials" data-section="testimonials">
|
<div id="testimonials" data-section="testimonials">
|
||||||
<TestimonialCardTwo
|
<TestimonialCardTwo
|
||||||
title="What Our Customers Say"
|
title="Success Stories from Our Users"
|
||||||
description="Join thousands of satisfied users who've transformed their web development workflow"
|
description="Hear from job seekers and employers who have found success through Jobee."
|
||||||
tag="Testimonials"
|
tag="User Testimonials"
|
||||||
|
tagIcon={Quote}
|
||||||
|
tagAnimation="slide-up"
|
||||||
textboxLayout="default"
|
textboxLayout="default"
|
||||||
useInvertedBackground={true}
|
useInvertedBackground={true}
|
||||||
animationType="slide-up"
|
animationType="slide-up"
|
||||||
testimonials={[
|
testimonials={[
|
||||||
{
|
{
|
||||||
id: "1", name: "Alex Chen", role: "Frontend Developer", testimonial: "This platform has completely transformed how I build websites. The features are intuitive and powerful."},
|
id: "1", name: "Sarah van der Berg", role: "Software Developer", testimonial: "I found my perfect job within 2 weeks using Jobee. The search filters made it easy to find remote opportunities in Amsterdam that matched my skill set.", imageSrc: "http://img.b2bpic.net/free-photo/front-view-smiley-man-posing_23-2150171293.jpg?_wi=1", imageAlt: "Sarah van der Berg"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "2", name: "Sarah Johnson", role: "Product Manager", testimonial: "Amazing tool that has saved our team countless hours. Highly recommended for any development team."},
|
id: "2", name: "Jan Pieterzoon", role: "HR Manager", testimonial: "As a recruiter, Jobee has revolutionized how we post jobs and connect with talented candidates across the Netherlands. The platform is intuitive and effective.", imageSrc: "http://img.b2bpic.net/free-photo/smiling-face-gorgeous-latin-american-woman_1262-5766.jpg?_wi=1", imageAlt: "Jan Pieterzoon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "3", name: "Emma Dijkstra", role: "Marketing Specialist", testimonial: "The application process on Jobee is so smooth. I applied for 5 positions and received 3 interview invitations within a week. Highly recommended!", imageSrc: "http://img.b2bpic.net/free-photo/smiling-senior-businessman-pointing-with-finger_1262-3108.jpg?_wi=1", imageAlt: "Emma Dijkstra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "4", name: "Michael Houtstra", role: "Finance Director", testimonial: "We've hired 12 excellent team members through Jobee in the past year. The quality of candidates is outstanding and the platform is incredibly easy to use.", imageSrc: "http://img.b2bpic.net/free-photo/studio-portrait-elegant-black-american-male-dressed-suit-grey-vignette-background_613910-9543.jpg?_wi=1", imageAlt: "Michael Houtstra"
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
|
buttonAnimation="slide-up"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="contact" data-section="contact">
|
<div id="contact" data-section="contact">
|
||||||
<ContactCenter
|
<ContactCenter
|
||||||
tag="Newsletter"
|
tag="Newsletter"
|
||||||
title="Stay Updated"
|
title="Stay Updated with Job Opportunities"
|
||||||
description="Subscribe to our newsletter for the latest updates, tips, and exclusive content."
|
description="Subscribe to our newsletter and get notified about new jobs matching your profile, industry trends, and career tips for the Dutch job market."
|
||||||
background={{ variant: "sparkles-gradient" }}
|
tagIcon={Mail}
|
||||||
|
tagAnimation="slide-up"
|
||||||
|
background={{
|
||||||
|
variant: "animated-grid"
|
||||||
|
}}
|
||||||
useInvertedBackground={false}
|
useInvertedBackground={false}
|
||||||
inputPlaceholder="Enter your email"
|
inputPlaceholder="Enter your email address"
|
||||||
buttonText="Subscribe"
|
buttonText="Subscribe"
|
||||||
termsText="We respect your privacy. Unsubscribe at any time."
|
termsText="We respect your privacy. Unsubscribe anytime from our newsletter."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="footer" data-section="footer">
|
<div id="footer" data-section="footer">
|
||||||
<FooterBase
|
<FooterBase
|
||||||
logoText="Webild"
|
logoText="Jobee"
|
||||||
copyrightText="© 2025 Webild. All rights reserved."
|
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
|
||||||
columns={footerColumns}
|
columns={footerColumns}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
import Input from "@/components/form/Input";
|
import { Briefcase, Mail } from "lucide-react";
|
||||||
import { Upload, CheckCircle, X } from "lucide-react";
|
import { useState } from "react";
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: "Search Jobs", id: "/search" },
|
{ name: "Search Jobs", id: "search" },
|
||||||
{ name: "Post a Job", id: "/post-job" },
|
{ name: "Post a Job", id: "/post-job" },
|
||||||
{ name: "Admin", id: "admin-login" },
|
{ name: "Applications", id: "/applications" },
|
||||||
{ name: "Browse", id: "/browse" },
|
{ name: "Browse", id: "browse" },
|
||||||
{ name: "Contact", id: "contact" },
|
{ name: "Contact", id: "contact" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -20,7 +19,7 @@ const footerColumns = [
|
|||||||
title: "Product", items: [
|
title: "Product", items: [
|
||||||
{ label: "Search Jobs", href: "/search" },
|
{ label: "Search Jobs", href: "/search" },
|
||||||
{ label: "Post a Job", href: "/post-job" },
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
{ label: "Browse by Province", href: "#provinces" },
|
{ label: "My Applications", href: "/applications" },
|
||||||
{ label: "For Employers", href: "#" },
|
{ label: "For Employers", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -42,88 +41,30 @@ const footerColumns = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
interface FormData {
|
|
||||||
jobTitle: string;
|
|
||||||
companyName: string;
|
|
||||||
location: string;
|
|
||||||
jobType: string;
|
|
||||||
salary: string;
|
|
||||||
description: string;
|
|
||||||
requirements: string;
|
|
||||||
benefits: string;
|
|
||||||
contactEmail: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FormErrors {
|
|
||||||
[key: string]: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PostJobPage() {
|
export default function PostJobPage() {
|
||||||
const [formData, setFormData] = useState<FormData>({
|
const [formData, setFormData] = useState({
|
||||||
jobTitle: "", companyName: "", location: "", jobType: "Full-time", salary: "", description: "", requirements: "", benefits: "", contactEmail: ""});
|
jobTitle: "", company: "", location: "", province: "", jobType: "Full-Time", salaryRange: "", description: "", requirements: "", contactEmail: "", benefits: ""});
|
||||||
|
|
||||||
const [errors, setErrors] = useState<FormErrors>({});
|
|
||||||
const [submitted, setSubmitted] = useState(false);
|
const [submitted, setSubmitted] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
const validateForm = (): boolean => {
|
const handleChange = (
|
||||||
const newErrors: FormErrors = {};
|
e: React.ChangeEvent<
|
||||||
|
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
|
||||||
if (!formData.jobTitle.trim()) {
|
>
|
||||||
newErrors.jobTitle = "Job title is required";
|
) => {
|
||||||
}
|
const { name, value } = e.target;
|
||||||
if (!formData.companyName.trim()) {
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||||
newErrors.companyName = "Company name is required";
|
|
||||||
}
|
|
||||||
if (!formData.location.trim()) {
|
|
||||||
newErrors.location = "Location is required";
|
|
||||||
}
|
|
||||||
if (!formData.salary.trim()) {
|
|
||||||
newErrors.salary = "Salary range is required";
|
|
||||||
}
|
|
||||||
if (!formData.description.trim() || formData.description.trim().length < 20) {
|
|
||||||
newErrors.description = "Job description must be at least 20 characters";
|
|
||||||
}
|
|
||||||
if (!formData.requirements.trim() || formData.requirements.trim().length < 20) {
|
|
||||||
newErrors.requirements = "Requirements must be at least 20 characters";
|
|
||||||
}
|
|
||||||
if (!formData.contactEmail.trim()) {
|
|
||||||
newErrors.contactEmail = "Contact email is required";
|
|
||||||
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.contactEmail)) {
|
|
||||||
newErrors.contactEmail = "Please enter a valid email address";
|
|
||||||
}
|
|
||||||
|
|
||||||
setErrors(newErrors);
|
|
||||||
return Object.keys(newErrors).length === 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleInputChange = (field: keyof FormData, value: string) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
setFormData((prev) => ({ ...prev, [field]: value }));
|
|
||||||
if (errors[field]) {
|
|
||||||
setErrors((prev) => {
|
|
||||||
const newErrors = { ...prev };
|
|
||||||
delete newErrors[field];
|
|
||||||
return newErrors;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!validateForm()) return;
|
console.log("Job Posted:", formData);
|
||||||
|
setSubmitted(true);
|
||||||
setIsLoading(true);
|
setTimeout(() => {
|
||||||
try {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
||||||
setSubmitted(true);
|
|
||||||
setFormData({
|
setFormData({
|
||||||
jobTitle: "", companyName: "", location: "", jobType: "Full-time", salary: "", description: "", requirements: "", benefits: "", contactEmail: ""});
|
jobTitle: "", company: "", location: "", province: "", jobType: "Full-Time", salaryRange: "", description: "", requirements: "", contactEmail: "", benefits: ""});
|
||||||
setTimeout(() => setSubmitted(false), 5000);
|
setSubmitted(false);
|
||||||
} catch (error) {
|
}, 2000);
|
||||||
console.error("Submission error:", error);
|
|
||||||
} finally {
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -148,213 +89,248 @@ export default function PostJobPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="min-h-screen bg-background py-16 md:py-24">
|
<div className="min-h-screen bg-gradient-to-b from-slate-50 to-slate-100 pt-32 pb-20">
|
||||||
<div className="mx-auto w-full max-w-2xl px-4 md:px-8">
|
<div className="max-w-2xl mx-auto px-4">
|
||||||
{/* Header */}
|
<div className="mb-8">
|
||||||
<div className="mb-12 text-center">
|
<div className="flex items-center gap-3 mb-4">
|
||||||
<h1 className="mb-4 text-4xl md:text-5xl font-bold text-foreground">
|
<Briefcase className="w-6 h-6 text-blue-600" />
|
||||||
Post a Job
|
<span className="text-sm font-semibold text-blue-600 uppercase tracking-wide">
|
||||||
|
Post Your Job
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-slate-900 mb-3">
|
||||||
|
Create Your Job Listing
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-lg text-foreground/75">
|
<p className="text-lg text-slate-600">
|
||||||
Reach thousands of qualified job seekers across the Netherlands
|
Fill in the details below to post your job opening and reach qualified candidates
|
||||||
|
across the Netherlands.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Success Message */}
|
|
||||||
{submitted && (
|
{submitted && (
|
||||||
<div className="mb-8 flex items-center gap-3 rounded-lg bg-green-50 p-4 text-green-700 border border-green-200">
|
<div className="mb-6 p-4 bg-green-50 border border-green-200 rounded-lg">
|
||||||
<CheckCircle size={20} />
|
<p className="text-green-800 font-semibold">✓ Job posted successfully!</p>
|
||||||
<div>
|
|
||||||
<p className="font-semibold">Job posted successfully!</p>
|
|
||||||
<p className="text-sm">Your job listing will be live shortly.</p>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={() => setSubmitted(false)}
|
|
||||||
className="ml-auto p-1 hover:bg-green-100 rounded"
|
|
||||||
>
|
|
||||||
<X size={16} />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Form */}
|
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className="rounded-xl bg-card p-8 md:p-12 border border-accent/20 shadow-lg"
|
className="bg-white rounded-lg shadow-lg p-8 space-y-6"
|
||||||
>
|
>
|
||||||
{/* Job Title */}
|
{/* Job Title */}
|
||||||
<div className="mb-6">
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="jobTitle" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Job Title *
|
Job Title *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<input
|
||||||
|
type="text"
|
||||||
|
id="jobTitle"
|
||||||
|
name="jobTitle"
|
||||||
value={formData.jobTitle}
|
value={formData.jobTitle}
|
||||||
onChange={(value) => handleInputChange("jobTitle", value)}
|
onChange={handleChange}
|
||||||
placeholder="e.g., Senior Software Engineer"
|
|
||||||
required
|
required
|
||||||
|
placeholder="e.g., Senior Software Developer"
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
{errors.jobTitle && (
|
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.jobTitle}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Company Name */}
|
{/* Company */}
|
||||||
<div className="mb-6">
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="company" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Company Name *
|
Company Name *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<input
|
||||||
value={formData.companyName}
|
type="text"
|
||||||
onChange={(value) => handleInputChange("companyName", value)}
|
id="company"
|
||||||
placeholder="Your company name"
|
name="company"
|
||||||
|
value={formData.company}
|
||||||
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
|
placeholder="Your company name"
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
{errors.companyName && (
|
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.companyName}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Location */}
|
{/* Location */}
|
||||||
<div className="mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<div>
|
||||||
Location *
|
<label htmlFor="location" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
</label>
|
City *
|
||||||
<Input
|
</label>
|
||||||
value={formData.location}
|
<input
|
||||||
onChange={(value) => handleInputChange("location", value)}
|
type="text"
|
||||||
placeholder="e.g., Amsterdam, Netherlands"
|
id="location"
|
||||||
required
|
name="location"
|
||||||
/>
|
value={formData.location}
|
||||||
{errors.location && (
|
onChange={handleChange}
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.location}</p>
|
required
|
||||||
)}
|
placeholder="e.g., Amsterdam"
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="province" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
|
Province *
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="province"
|
||||||
|
name="province"
|
||||||
|
value={formData.province}
|
||||||
|
onChange={handleChange}
|
||||||
|
required
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
>
|
||||||
|
<option value="">Select Province</option>
|
||||||
|
<option value="North Holland">North Holland</option>
|
||||||
|
<option value="South Holland">South Holland</option>
|
||||||
|
<option value="Utrecht">Utrecht</option>
|
||||||
|
<option value="Gelderland">Gelderland</option>
|
||||||
|
<option value="North Brabant">North Brabant</option>
|
||||||
|
<option value="Overijssel">Overijssel</option>
|
||||||
|
<option value="Flevoland">Flevoland</option>
|
||||||
|
<option value="Friesland">Friesland</option>
|
||||||
|
<option value="Groningen">Groningen</option>
|
||||||
|
<option value="Drenthe">Drenthe</option>
|
||||||
|
<option value="Limburg">Limburg</option>
|
||||||
|
<option value="Zeeland">Zeeland</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Job Type and Salary */}
|
{/* Job Type */}
|
||||||
<div className="mb-6 grid gap-6 md:grid-cols-2">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="jobType" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Job Type *
|
Job Type *
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
|
id="jobType"
|
||||||
|
name="jobType"
|
||||||
value={formData.jobType}
|
value={formData.jobType}
|
||||||
onChange={(e) => handleInputChange("jobType", e.target.value)}
|
onChange={handleChange}
|
||||||
className="w-full rounded-lg bg-secondary-button px-4 py-2.5 text-foreground placeholder-foreground/75 border border-accent/20 focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
>
|
>
|
||||||
<option>Full-time</option>
|
<option value="Full-Time">Full-Time</option>
|
||||||
<option>Part-time</option>
|
<option value="Part-Time">Part-Time</option>
|
||||||
<option>Contract</option>
|
<option value="Contract">Contract</option>
|
||||||
<option>Temporary</option>
|
<option value="Temporary">Temporary</option>
|
||||||
<option>Internship</option>
|
<option value="Internship">Internship</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="salaryRange" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Salary Range *
|
Salary Range
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<input
|
||||||
value={formData.salary}
|
type="text"
|
||||||
onChange={(value) => handleInputChange("salary", value)}
|
id="salaryRange"
|
||||||
placeholder="e.g., €50,000 - €80,000"
|
name="salaryRange"
|
||||||
required
|
value={formData.salaryRange}
|
||||||
|
onChange={handleChange}
|
||||||
|
placeholder="e.g., €2,000 - €3,500"
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
{errors.salary && (
|
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.salary}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Job Description */}
|
{/* Description */}
|
||||||
<div className="mb-6">
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="description" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Job Description *
|
Job Description *
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id="description"
|
||||||
|
name="description"
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) => handleInputChange("description", e.target.value)}
|
onChange={handleChange}
|
||||||
placeholder="Describe the role, responsibilities, and key details..."
|
|
||||||
rows={6}
|
|
||||||
className="w-full rounded-lg bg-secondary-button px-4 py-2.5 text-foreground placeholder-foreground/75 border border-accent/20 focus:outline-none focus:ring-2 focus:ring-primary-cta resize-none"
|
|
||||||
required
|
required
|
||||||
|
rows={4}
|
||||||
|
placeholder="Describe the role, responsibilities, and key tasks..."
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
{errors.description && (
|
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.description}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Requirements */}
|
{/* Requirements */}
|
||||||
<div className="mb-6">
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="requirements" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Requirements *
|
Requirements *
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id="requirements"
|
||||||
|
name="requirements"
|
||||||
value={formData.requirements}
|
value={formData.requirements}
|
||||||
onChange={(e) => handleInputChange("requirements", e.target.value)}
|
onChange={handleChange}
|
||||||
placeholder="List the key requirements and qualifications (e.g., skills, experience, education)..."
|
|
||||||
rows={5}
|
|
||||||
className="w-full rounded-lg bg-secondary-button px-4 py-2.5 text-foreground placeholder-foreground/75 border border-accent/20 focus:outline-none focus:ring-2 focus:ring-primary-cta resize-none"
|
|
||||||
required
|
required
|
||||||
|
rows={4}
|
||||||
|
placeholder="List the required skills, experience, education, and qualifications..."
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
{errors.requirements && (
|
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.requirements}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Benefits */}
|
{/* Benefits */}
|
||||||
<div className="mb-6">
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="benefits" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Benefits (Optional)
|
Benefits & Perks
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
id="benefits"
|
||||||
|
name="benefits"
|
||||||
value={formData.benefits}
|
value={formData.benefits}
|
||||||
onChange={(e) => handleInputChange("benefits", e.target.value)}
|
onChange={handleChange}
|
||||||
placeholder="Highlight benefits such as remote work, flexible hours, health insurance, training, etc."
|
rows={3}
|
||||||
rows={4}
|
placeholder="List benefits such as health insurance, remote work, stock options, etc..."
|
||||||
className="w-full rounded-lg bg-secondary-button px-4 py-2.5 text-foreground placeholder-foreground/75 border border-accent/20 focus:outline-none focus:ring-2 focus:ring-primary-cta resize-none"
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Contact Email */}
|
{/* Contact Email */}
|
||||||
<div className="mb-8">
|
<div>
|
||||||
<label className="block mb-2 text-sm font-semibold text-foreground">
|
<label htmlFor="contactEmail" className="block text-sm font-semibold text-slate-900 mb-2">
|
||||||
Contact Email *
|
Contact Email *
|
||||||
</label>
|
</label>
|
||||||
<Input
|
<input
|
||||||
value={formData.contactEmail}
|
|
||||||
onChange={(value) => handleInputChange("contactEmail", value)}
|
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="your-email@company.com"
|
id="contactEmail"
|
||||||
|
name="contactEmail"
|
||||||
|
value={formData.contactEmail}
|
||||||
|
onChange={handleChange}
|
||||||
required
|
required
|
||||||
|
placeholder="recruiter@company.com"
|
||||||
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
/>
|
/>
|
||||||
{errors.contactEmail && (
|
|
||||||
<p className="mt-1 text-sm text-red-600">{errors.contactEmail}</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Submit Button */}
|
{/* Submit Button */}
|
||||||
<button
|
<div className="flex gap-4 pt-6">
|
||||||
type="submit"
|
<button
|
||||||
disabled={isLoading}
|
type="submit"
|
||||||
className="w-full rounded-lg bg-primary-cta px-6 py-3 font-semibold text-white hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed transition-opacity flex items-center justify-center gap-2"
|
className="flex-1 bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg transition duration-200"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
Post Job
|
||||||
<>
|
</button>
|
||||||
<div className="h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent"></div>
|
<button
|
||||||
Posting...
|
type="button"
|
||||||
</>
|
onClick={() =>
|
||||||
) : (
|
setFormData({
|
||||||
<>
|
jobTitle: "", company: "", location: "", province: "", jobType: "Full-Time", salaryRange: "", description: "", requirements: "", contactEmail: "", benefits: ""})
|
||||||
<Upload size={18} />
|
}
|
||||||
Post Job
|
className="flex-1 bg-slate-200 hover:bg-slate-300 text-slate-900 font-semibold py-3 rounded-lg transition duration-200"
|
||||||
</>
|
>
|
||||||
)}
|
Clear Form
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
{/* Form Info */}
|
|
||||||
<p className="mt-4 text-center text-sm text-foreground/60">
|
|
||||||
* Required fields. Your job will be reviewed before going live.
|
|
||||||
</p>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<div className="mt-8 p-6 bg-blue-50 border border-blue-200 rounded-lg">
|
||||||
|
<h3 className="text-lg font-semibold text-slate-900 mb-2">✓ Tips for a Great Job Listing</h3>
|
||||||
|
<ul className="text-slate-700 space-y-1">
|
||||||
|
<li>• Use clear, descriptive job titles</li>
|
||||||
|
<li>• Include specific requirements and qualifications</li>
|
||||||
|
<li>• Highlight company culture and benefits</li>
|
||||||
|
<li>• Provide accurate salary ranges</li>
|
||||||
|
<li>• Ensure contact information is correct</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,103 +1,357 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState, useMemo } from "react";
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||||
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
|
||||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||||
import { useState } from "react";
|
import { Search, ChevronDown, MapPin, DollarSign, Briefcase } from "lucide-react";
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ name: "Dashboard", id: "/" },
|
{ name: "Search Jobs", id: "/search" },
|
||||||
{ name: "Browse Jobs", id: "/search" },
|
{ name: "Post a Job", id: "/post-job" },
|
||||||
{ name: "My Applications", id: "/applications" },
|
{ name: "Admin", id: "/admin-login" },
|
||||||
{ name: "Settings", id: "#settings" },
|
{ name: "Browse", id: "browse" },
|
||||||
|
{ name: "Contact", id: "contact" },
|
||||||
];
|
];
|
||||||
|
|
||||||
const footerColumns = [
|
const footerColumns = [
|
||||||
{
|
{
|
||||||
title: "Product", items: [
|
title: "Product", items: [
|
||||||
{ label: "Browse Jobs", href: "/search" },
|
{ label: "Search Jobs", href: "/search" },
|
||||||
{ label: "Companies", href: "#" },
|
{ label: "Post a Job", href: "/post-job" },
|
||||||
|
{ label: "Browse by Province", href: "#provinces" },
|
||||||
{ label: "For Employers", href: "#" },
|
{ label: "For Employers", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Company", items: [
|
title: "Company", items: [
|
||||||
{ label: "About", href: "#" },
|
{ label: "About Jobee", href: "#about" },
|
||||||
|
{ label: "Careers", href: "#" },
|
||||||
|
{ label: "Contact Us", href: "#contact" },
|
||||||
{ label: "Blog", href: "#" },
|
{ label: "Blog", href: "#" },
|
||||||
{ label: "Contact", href: "#" },
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Resources", items: [
|
||||||
|
{ label: "Privacy Policy", href: "#" },
|
||||||
|
{ label: "Terms of Service", href: "#" },
|
||||||
|
{ label: "FAQ", href: "#" },
|
||||||
|
{ label: "Support", href: "#" },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const mockJobs = [
|
||||||
|
{
|
||||||
|
id: "1", title: "Senior Software Engineer", company: "TechCorp Amsterdam", location: "Amsterdam", province: "North Holland", salary: "€80,000 - €120,000", salaryMin: 80000,
|
||||||
|
salaryMax: 120000,
|
||||||
|
jobType: "Full-time", category: "Technology", description:
|
||||||
|
"Join our innovative team as a Senior Software Engineer. We\'re looking for experienced developers with expertise in React, Node.js, and cloud technologies.", logo: "http://img.b2bpic.net/free-vector/tech-company-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "2", title: "Marketing Manager", company: "Creative Solutions Rotterdam", location: "Rotterdam", province: "South Holland", salary: "€60,000 - €85,000", salaryMin: 60000,
|
||||||
|
salaryMax: 85000,
|
||||||
|
jobType: "Full-time", category: "Marketing", description:
|
||||||
|
"Lead our marketing initiatives and drive brand growth. We seek a strategic marketer with experience in digital marketing and campaign management.", logo: "http://img.b2bpic.net/free-vector/marketing-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "3", title: "Data Scientist", company: "Analytics Pro Utrecht", location: "Utrecht", province: "Utrecht", salary: "€70,000 - €110,000", salaryMin: 70000,
|
||||||
|
salaryMax: 110000,
|
||||||
|
jobType: "Full-time", category: "Data Science", description:
|
||||||
|
"Develop advanced analytics solutions for our global clients. Expertise in Python, machine learning, and big data technologies required.", logo: "http://img.b2bpic.net/free-vector/analytics-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "4", title: "UX/UI Designer", company: "Design Studios The Hague", location: "The Hague", province: "South Holland", salary: "€55,000 - €75,000", salaryMin: 55000,
|
||||||
|
salaryMax: 75000,
|
||||||
|
jobType: "Full-time", category: "Design", description:
|
||||||
|
"Create beautiful and intuitive user experiences for our web and mobile applications. Portfolio required.", logo: "http://img.b2bpic.net/free-vector/design-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "5", title: "Sales Executive", company: "Enterprise Solutions Groningen", location: "Groningen", province: "Groningen", salary: "€45,000 - €70,000", salaryMin: 45000,
|
||||||
|
salaryMax: 70000,
|
||||||
|
jobType: "Full-time", category: "Sales", description:
|
||||||
|
"Grow our sales pipeline and build strong client relationships. Commission and bonus structure available.", logo: "http://img.b2bpic.net/free-vector/sales-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "6", title: "HR Specialist", company: "People First Leiden", location: "Leiden", province: "South Holland", salary: "€50,000 - €65,000", salaryMin: 50000,
|
||||||
|
salaryMax: 65000,
|
||||||
|
jobType: "Part-time", category: "Human Resources", description:
|
||||||
|
"Support our HR team in recruitment, onboarding, and employee development initiatives.", logo: "http://img.b2bpic.net/free-vector/hr-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "7", title: "DevOps Engineer", company: "Cloud Innovations Eindhoven", location: "Eindhoven", province: "North Brabant", salary: "€75,000 - €105,000", salaryMin: 75000,
|
||||||
|
salaryMax: 105000,
|
||||||
|
jobType: "Full-time", category: "Technology", description:
|
||||||
|
"Manage and optimize our cloud infrastructure. Experience with Docker, Kubernetes, and CI/CD pipelines required.", logo: "http://img.b2bpic.net/free-vector/devops-logo_23-2148947635.jpg"},
|
||||||
|
{
|
||||||
|
id: "8", title: "Product Manager", company: "Innovation Lab Delft", location: "Delft", province: "South Holland", salary: "€70,000 - €95,000", salaryMin: 70000,
|
||||||
|
salaryMax: 95000,
|
||||||
|
jobType: "Full-time", category: "Product", description:
|
||||||
|
"Lead product strategy and roadmap development for our SaaS platform. Experience with agile methodologies required.", logo: "http://img.b2bpic.net/free-vector/product-logo_23-2148947635.jpg"},
|
||||||
|
];
|
||||||
|
|
||||||
|
const provinces = [
|
||||||
|
"All Provinces", "North Holland", "South Holland", "Utrecht", "Groningen", "North Brabant", "Limburg", "Friesland", "Drenthe", "Flevoland", "Overijssel", "Gelderland"];
|
||||||
|
|
||||||
|
const categories = [
|
||||||
|
"All Categories", "Technology", "Marketing", "Sales", "Design", "Data Science", "Human Resources", "Product"];
|
||||||
|
|
||||||
|
const jobTypes = ["All Types", "Full-time", "Part-time", "Contract", "Freelance"];
|
||||||
|
|
||||||
export default function SearchPage() {
|
export default function SearchPage() {
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
const [selectedProvince, setSelectedProvince] = useState("All Provinces");
|
||||||
|
const [selectedCategory, setSelectedCategory] = useState("All Categories");
|
||||||
|
const [selectedJobType, setSelectedJobType] = useState("All Types");
|
||||||
|
const [sortBy, setSortBy] = useState("relevant");
|
||||||
|
const [minSalary, setMinSalary] = useState(0);
|
||||||
|
const [maxSalary, setMaxSalary] = useState(150000);
|
||||||
|
|
||||||
const handleSearch = (callback: () => void) => {
|
const filteredJobs = useMemo(() => {
|
||||||
callback();
|
let filtered = mockJobs.filter((job) => {
|
||||||
};
|
const matchesSearch =
|
||||||
|
job.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
job.company.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||||
|
job.description.toLowerCase().includes(searchQuery.toLowerCase());
|
||||||
|
|
||||||
|
const matchesProvince =
|
||||||
|
selectedProvince === "All Provinces" ||
|
||||||
|
job.province === selectedProvince;
|
||||||
|
|
||||||
|
const matchesCategory =
|
||||||
|
selectedCategory === "All Categories" ||
|
||||||
|
job.category === selectedCategory;
|
||||||
|
|
||||||
|
const matchesJobType =
|
||||||
|
selectedJobType === "All Types" || job.jobType === selectedJobType;
|
||||||
|
|
||||||
|
const matchesSalary =
|
||||||
|
job.salaryMin >= minSalary && job.salaryMax <= maxSalary;
|
||||||
|
|
||||||
|
return (
|
||||||
|
matchesSearch &&
|
||||||
|
matchesProvince &&
|
||||||
|
matchesCategory &&
|
||||||
|
matchesJobType &&
|
||||||
|
matchesSalary
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sortBy === "salary-high") {
|
||||||
|
filtered.sort((a, b) => b.salaryMax - a.salaryMax);
|
||||||
|
} else if (sortBy === "salary-low") {
|
||||||
|
filtered.sort((a, b) => a.salaryMin - b.salaryMin);
|
||||||
|
} else if (sortBy === "recent") {
|
||||||
|
filtered.reverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}, [
|
||||||
|
searchQuery,
|
||||||
|
selectedProvince,
|
||||||
|
selectedCategory,
|
||||||
|
selectedJobType,
|
||||||
|
minSalary,
|
||||||
|
maxSalary,
|
||||||
|
sortBy,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<ThemeProvider
|
||||||
defaultButtonVariant="text-stagger"
|
defaultButtonVariant="text-stagger"
|
||||||
defaultTextAnimation="reveal-blur"
|
defaultTextAnimation="reveal-blur"
|
||||||
borderRadius="pill"
|
borderRadius="pill"
|
||||||
contentWidth="medium"
|
contentWidth="smallMedium"
|
||||||
sizing="medium"
|
sizing="mediumLargeSizeLargeTitles"
|
||||||
background="circleGradient"
|
background="circleGradient"
|
||||||
cardStyle="glass-elevated"
|
cardStyle="gradient-radial"
|
||||||
primaryButtonStyle="gradient"
|
primaryButtonStyle="double-inset"
|
||||||
secondaryButtonStyle="glass"
|
secondaryButtonStyle="glass"
|
||||||
headingFontWeight="normal"
|
headingFontWeight="bold"
|
||||||
>
|
>
|
||||||
<div id="nav" data-section="nav">
|
<div id="nav" data-section="nav">
|
||||||
<NavbarStyleCentered
|
<NavbarStyleCentered
|
||||||
brandName="Jobee"
|
brandName="Jobee"
|
||||||
navItems={navItems}
|
navItems={navItems}
|
||||||
button={{
|
button={{
|
||||||
text: "Sign In", onClick: () => console.log("sign-in"),
|
text: "Post a Job", href: "/post-job"}}
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="search" data-section="search" className="py-20">
|
<div className="min-h-screen bg-gradient-to-b from-background to-background-accent pt-20 pb-20">
|
||||||
<div className="container mx-auto px-4">
|
<div className="max-w-7xl mx-auto px-4">
|
||||||
<h1 className="text-4xl font-bold mb-4 text-center">Find Your Dream Job</h1>
|
{/* Search Header */}
|
||||||
<p className="text-lg text-gray-600 text-center mb-8">Search across all job listings in the Netherlands</p>
|
<div className="mb-12">
|
||||||
|
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-4">
|
||||||
<div className="max-w-2xl mx-auto mb-12">
|
Find Your Dream Job
|
||||||
<div className="flex gap-4">
|
</h1>
|
||||||
|
<p className="text-lg text-foreground/70 mb-8">
|
||||||
|
Discover thousands of opportunities across the Netherlands
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Main Search Bar */}
|
||||||
|
<div className="relative mb-8">
|
||||||
|
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-foreground/50" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search jobs..."
|
placeholder="Search by job title, company, or keywords..."
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="flex-1 px-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full pl-12 pr-4 py-4 rounded-full bg-card border border-accent/20 text-foreground placeholder-foreground/50 focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
onClick={() => handleSearch(() => console.log("Searching for:", searchQuery))}
|
|
||||||
className="px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
|
|
||||||
>
|
|
||||||
Search
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Filter Bar */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-4 mb-8">
|
||||||
|
{/* Province Filter */}
|
||||||
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={selectedProvince}
|
||||||
|
onChange={(e) => setSelectedProvince(e.target.value)}
|
||||||
|
className="w-full px-4 py-3 rounded-lg bg-card border border-accent/20 text-foreground cursor-pointer appearance-none pr-10 focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||||
|
>
|
||||||
|
{provinces.map((province) => (
|
||||||
|
<option key={province} value={province}>
|
||||||
|
{province}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<MapPin className="absolute right-3 top-1/2 transform -translate-y-1/2 text-foreground/50 pointer-events-none" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Category Filter */}
|
||||||
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={selectedCategory}
|
||||||
|
onChange={(e) => setSelectedCategory(e.target.value)}
|
||||||
|
className="w-full px-4 py-3 rounded-lg bg-card border border-accent/20 text-foreground cursor-pointer appearance-none pr-10 focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||||
|
>
|
||||||
|
{categories.map((category) => (
|
||||||
|
<option key={category} value={category}>
|
||||||
|
{category}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<Briefcase className="absolute right-3 top-1/2 transform -translate-y-1/2 text-foreground/50 pointer-events-none" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Job Type Filter */}
|
||||||
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={selectedJobType}
|
||||||
|
onChange={(e) => setSelectedJobType(e.target.value)}
|
||||||
|
className="w-full px-4 py-3 rounded-lg bg-card border border-accent/20 text-foreground cursor-pointer appearance-none pr-10 focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||||
|
>
|
||||||
|
{jobTypes.map((type) => (
|
||||||
|
<option key={type} value={type}>
|
||||||
|
{type}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<ChevronDown className="absolute right-3 top-1/2 transform -translate-y-1/2 text-foreground/50 pointer-events-none" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Salary Range */}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<DollarSign className="text-foreground/50" />
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min="0"
|
||||||
|
max="150000"
|
||||||
|
step="10000"
|
||||||
|
value={maxSalary}
|
||||||
|
onChange={(e) => setMaxSalary(parseInt(e.target.value))}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Sort By */}
|
||||||
|
<div className="relative">
|
||||||
|
<select
|
||||||
|
value={sortBy}
|
||||||
|
onChange={(e) => setSortBy(e.target.value)}
|
||||||
|
className="w-full px-4 py-3 rounded-lg bg-card border border-accent/20 text-foreground cursor-pointer appearance-none pr-10 focus:outline-none focus:ring-2 focus:ring-primary-cta"
|
||||||
|
>
|
||||||
|
<option value="relevant">Most Relevant</option>
|
||||||
|
<option value="recent">Most Recent</option>
|
||||||
|
<option value="salary-high">Highest Salary</option>
|
||||||
|
<option value="salary-low">Lowest Salary</option>
|
||||||
|
</select>
|
||||||
|
<ChevronDown className="absolute right-3 top-1/2 transform -translate-y-1/2 text-foreground/50 pointer-events-none" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Results Count */}
|
||||||
|
<p className="text-sm text-foreground/60">
|
||||||
|
Showing {filteredJobs.length} of {mockJobs.length} jobs
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
{/* Job Listings */}
|
||||||
<div className="p-6 bg-white rounded-lg shadow-md border border-gray-200">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
<h3 className="text-xl font-semibold mb-2">Senior Software Engineer</h3>
|
{filteredJobs.length > 0 ? (
|
||||||
<p className="text-gray-600 mb-4">Amsterdam, Netherlands · Full-time</p>
|
filteredJobs.map((job) => (
|
||||||
<p className="text-sm text-gray-500">Posted 2 days ago</p>
|
<div
|
||||||
</div>
|
key={job.id}
|
||||||
<div className="p-6 bg-white rounded-lg shadow-md border border-gray-200">
|
className="p-6 rounded-xl bg-card border border-accent/20 hover:border-accent/50 transition-all duration-300 hover:shadow-lg cursor-pointer"
|
||||||
<h3 className="text-xl font-semibold mb-2">Product Manager</h3>
|
>
|
||||||
<p className="text-gray-600 mb-4">Rotterdam, Netherlands · Full-time</p>
|
<div className="flex items-start justify-between mb-4">
|
||||||
<p className="text-sm text-gray-500">Posted 5 days ago</p>
|
<div className="flex items-start gap-4 flex-1">
|
||||||
</div>
|
<div className="w-16 h-16 rounded-lg bg-gradient-to-br from-primary-cta to-secondary-cta flex items-center justify-center text-white font-bold text-xl flex-shrink-0">
|
||||||
<div className="p-6 bg-white rounded-lg shadow-md border border-gray-200">
|
{job.company.charAt(0)}
|
||||||
<h3 className="text-xl font-semibold mb-2">UX Designer</h3>
|
</div>
|
||||||
<p className="text-gray-600 mb-4">Utrecht, Netherlands · Full-time</p>
|
<div className="flex-1">
|
||||||
<p className="text-sm text-gray-500">Posted 1 week ago</p>
|
<h3 className="text-xl font-bold text-foreground mb-1">
|
||||||
</div>
|
{job.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-foreground/70 mb-2">
|
||||||
|
{job.company}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-foreground/60">
|
||||||
|
{job.location}, {job.province}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-right">
|
||||||
|
<p className="text-lg font-bold text-primary-cta mb-2">
|
||||||
|
{job.salary}
|
||||||
|
</p>
|
||||||
|
<span className="inline-block px-3 py-1 rounded-full text-xs font-semibold bg-primary-cta/10 text-primary-cta">
|
||||||
|
{job.jobType}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-sm text-foreground/70 mb-4 line-clamp-2">
|
||||||
|
{job.description}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<span className="inline-block px-3 py-1 rounded-lg text-xs bg-background text-foreground/70">
|
||||||
|
{job.category}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<button className="px-4 py-2 rounded-full bg-primary-cta text-white font-semibold hover:opacity-90 transition-opacity">
|
||||||
|
View Details
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<p className="text-lg text-foreground/60 mb-4">
|
||||||
|
No jobs found matching your criteria.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setSearchQuery("");
|
||||||
|
setSelectedProvince("All Provinces");
|
||||||
|
setSelectedCategory("All Categories");
|
||||||
|
setSelectedJobType("All Types");
|
||||||
|
setMinSalary(0);
|
||||||
|
setMaxSalary(150000);
|
||||||
|
}}
|
||||||
|
className="px-6 py-2 rounded-full bg-primary-cta text-white font-semibold hover:opacity-90 transition-opacity"
|
||||||
|
>
|
||||||
|
Clear Filters
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -105,7 +359,7 @@ export default function SearchPage() {
|
|||||||
<div id="footer" data-section="footer">
|
<div id="footer" data-section="footer">
|
||||||
<FooterBase
|
<FooterBase
|
||||||
logoText="Jobee"
|
logoText="Jobee"
|
||||||
copyrightText="© 2025 Jobee"
|
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
|
||||||
columns={footerColumns}
|
columns={footerColumns}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,15 +10,15 @@
|
|||||||
--accent: #ffffff;
|
--accent: #ffffff;
|
||||||
--background-accent: #ffffff; */
|
--background-accent: #ffffff; */
|
||||||
|
|
||||||
--background: #f5f5f5;
|
--background: #ffffff;
|
||||||
--card: #ffffff;
|
--card: #f9f9f9;
|
||||||
--foreground: #1c1c1c;
|
--foreground: #000612e6;
|
||||||
--primary-cta: #1c1c1c;
|
--primary-cta: #15479c;
|
||||||
--primary-cta-text: #f5f5f5;
|
--primary-cta-text: #f5f5f5;
|
||||||
--secondary-cta: #ffffff;
|
--secondary-cta: #f9f9f9;
|
||||||
--secondary-cta-text: #1c1c1c;
|
--secondary-cta-text: #1c1c1c;
|
||||||
--accent: #15479c;
|
--accent: #e2e2e2;
|
||||||
--background-accent: #a8cce8;
|
--background-accent: #c4c4c4;
|
||||||
|
|
||||||
/* text sizing - set by ThemeProvider */
|
/* text sizing - set by ThemeProvider */
|
||||||
/* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);
|
/* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem);
|
||||||
|
|||||||
Reference in New Issue
Block a user