Update src/app/search/page.tsx

This commit is contained in:
2026-03-08 22:38:54 +00:00
parent 18e1e5d5ef
commit a4f5c9df37

View File

@@ -1,16 +1,15 @@
"use client";
import { useState, useMemo } from "react";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
import FeatureCardEight from "@/components/sections/feature/FeatureCardEight";
import ContactCenter from "@/components/sections/contact/ContactCenter";
import FooterBase from "@/components/sections/footer/FooterBase";
import { Sparkles, Mail } from "lucide-react";
import { Search, ChevronDown, MapPin, DollarSign, Briefcase } from "lucide-react";
const navItems = [
{ name: "Search Jobs", id: "search" },
{ name: "Post a Job", id: "post-job" },
{ name: "Admin", id: "admin-login" },
{ 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" },
];
@@ -42,7 +41,115 @@ const footerColumns = [
},
];
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() {
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 filteredJobs = useMemo(() => {
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 (
<ThemeProvider
defaultButtonVariant="text-stagger"
@@ -65,54 +172,188 @@ export default function SearchPage() {
/>
</div>
<div id="features" data-section="features">
<FeatureCardEight
title="Search & Browse Jobs Across the Netherlands"
description="Explore our comprehensive job search experience with advanced filters, diverse listings, and tailored opportunities for every career stage."
tag="Advanced Search"
tagIcon={Sparkles}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
features={[
{
id: 1,
title: "Filter by Province", description:
"Search jobs from all 12 Dutch provinces including Amsterdam, Rotterdam, Utrecht, and beyond. Find opportunities near you or explore remote positions nationwide.", imageSrc:
"http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=4", imageAlt: "Province filter interface"},
{
id: 2,
title: "Refine by Category", description:
"Browse across multiple industries: Technology, Healthcare, Finance, Engineering, Marketing, Sales, and more. Find roles that match your expertise and interests.", imageSrc:
"http://img.b2bpic.net/free-vector/professional-bookkeeping-postcard-template_23-2149341358.jpg?_wi=2", imageAlt: "Job category options"},
{
id: 3,
title: "Salary & Experience Level", description:
"Filter by salary range, job type (full-time, part-time, contract), and experience level (entry-level, mid-career, senior) to find the perfect match for your career goals.", imageSrc:
"http://img.b2bpic.net/free-photo/corporate-workers-brainstorming-together_23-2148804568.jpg?_wi=3", imageAlt: "Salary and level filters"},
]}
buttons={[
{
text: "Start Your Search", href: "/search"},
]}
buttonAnimation="slide-up"
/>
</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">
{/* Search Header */}
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-foreground mb-4">
Find Your Dream Job
</h1>
<p className="text-lg text-foreground/70 mb-8">
Discover thousands of opportunities across the Netherlands
</p>
<div id="contact" data-section="contact">
<ContactCenter
tag="Save Your Search"
title="Get Personalized Job Recommendations"
description="Create an account and set up job alerts. We'll notify you about new positions matching your criteria so you never miss an opportunity."
tagIcon={Mail}
tagAnimation="slide-up"
background={{
variant: "animated-grid"}}
useInvertedBackground={false}
inputPlaceholder="Enter your email to get started"
buttonText="Create Alert"
termsText="Your preferences are private and secure. Manage your alerts anytime."
/>
{/* 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
type="text"
placeholder="Search by job title, company, or keywords..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
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"
/>
</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>
{/* Job Listings */}
<div className="grid grid-cols-1 gap-4">
{filteredJobs.length > 0 ? (
filteredJobs.map((job) => (
<div
key={job.id}
className="p-6 rounded-xl bg-card border border-accent/20 hover:border-accent/50 transition-all duration-300 hover:shadow-lg cursor-pointer"
>
<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">
{job.company.charAt(0)}
</div>
<div className="flex-1">
<h3 className="text-xl font-bold text-foreground mb-1">
{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 id="footer" data-section="footer">
@@ -124,4 +365,4 @@ export default function SearchPage() {
</div>
</ThemeProvider>
);
}
}