Merge version_2 into main

Merge version_2 into main
This commit was merged in pull request #4.
This commit is contained in:
2026-03-08 22:12:44 +00:00
6 changed files with 1222 additions and 1550 deletions

246
src/app/admin/page.tsx Normal file
View File

@@ -0,0 +1,246 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
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 {
Users,
Briefcase,
BarChart3,
Shield,
Activity,
TrendingUp,
AlertCircle,
Settings,
} from "lucide-react";
const navItems = [
{ name: "Dashboard", id: "/admin" },
{ name: "Jobs", id: "/admin#jobs" },
{ name: "Users", id: "/admin#users" },
{ name: "Moderation", id: "/admin#moderation" },
{ name: "Analytics", id: "/admin#analytics" },
];
const footerColumns = [
{
title: "Admin", items: [
{ label: "Dashboard", href: "/admin" },
{ label: "Settings", href: "/admin#settings" },
{ label: "Reports", href: "/admin#reports" },
],
},
{
title: "Quick Links", items: [
{ label: "Help", href: "#" },
{ label: "Support", href: "#" },
{ label: "Documentation", href: "#" },
],
},
];
export default function AdminPage() {
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 Admin"
navItems={navItems}
button={{
text: "Logout", onClick: () => console.log("logout"),
}}
/>
</div>
<div id="jobs" data-section="jobs" className="pt-20">
<MetricCardTen
title="Job Management"
description="Monitor and manage all job postings across the platform"
tag="Active Jobs"
textboxLayout="default"
useInvertedBackground={false}
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">
<MetricCardTen
title="User Management"
description="Overview of registered users, job seekers, and employers"
tag="Active Users"
textboxLayout="default"
useInvertedBackground={true}
animationType="slide-up"
metrics={[
{
id: "1", title: "Sarah van der Berg", subtitle: "Amsterdam, Netherlands · Software Developer", category: "Job Seeker", value: "Active", buttons: [
{ text: "View Profile", href: "#" },
{ text: "Manage", href: "#" },
],
},
{
id: "2", title: "Acme Corporation", subtitle: "Rotterdam, Netherlands · Tech Company", category: "Employer", value: "Verified", buttons: [
{ text: "View", href: "#" },
{ text: "Manage", href: "#" },
],
},
{
id: "3", title: "Jan Pieterzoon", subtitle: "Utrecht, Netherlands · HR Manager", category: "Employer", value: "Active", buttons: [
{ text: "View Profile", href: "#" },
{ text: "Manage", href: "#" },
],
},
{
id: "4", title: "Emma Dijkstra", subtitle: "The Hague, Netherlands · Marketing Specialist", category: "Job Seeker", value: "Active", buttons: [
{ text: "View Profile", href: "#" },
{ text: "Manage", href: "#" },
],
},
]}
/>
</div>
<div id="analytics" data-section="analytics">
<FeatureBento
title="Platform Analytics & Insights"
description="Monitor key metrics and platform performance across all regions"
tag="Real-time Data"
tagIcon={Activity}
textboxLayout="default"
useInvertedBackground={false}
animationType="slide-up"
carouselMode="buttons"
features={[
{
id: 1,
title: "Job Applications Trend", description: "Track weekly application volume and trends", bentoComponent: "line-chart"},
{
id: 2,
title: "Revenue by Province", description: "Revenue distribution across Dutch provinces", bentoComponent: "animated-bar-chart"},
{
id: 3,
title: "User Growth Metrics", description: "Active users and engagement statistics", bentoComponent: "icon-info-cards", items: [
{ icon: Users, label: "Total Users", value: "24,500+" },
{ icon: Briefcase, label: "Job Postings", value: "3,240" },
{ icon: TrendingUp, label: "Monthly Growth", value: "12.5%" },
],
},
{
id: 4,
title: "Geographic Distribution", description: "Job seeker and employer locations worldwide", bentoComponent: "map"},
{
id: 5,
title: "Popular Job Categories", description: "Most sought-after job positions and skills", bentoComponent: "marquee", centerIcon: Briefcase,
variant: "text", texts: [
"Software Engineering", "Product Management", "Data Science", "UX Design", "Marketing", "Sales"],
},
{
id: 6,
title: "System Status Overview", description: "Platform health and service monitoring", bentoComponent: "3d-stack-cards", items: [
{
icon: Activity,
title: "API Health", subtitle: "99.9% Uptime", detail: "All systems operational"},
{
icon: BarChart3,
title: "Database", subtitle: "256 GB", detail: "45% capacity used"},
{
icon: Shield,
title: "Security", subtitle: "Secure", detail: "No active threats"},
],
},
]}
/>
</div>
<div id="moderation" data-section="moderation">
<MetricCardTen
title="Content Moderation"
description="Review and manage flagged content, reports, and user complaints"
tag="Flagged Content"
textboxLayout="default"
useInvertedBackground={true}
animationType="slide-up"
metrics={[
{
id: "1", title: "Job Post: Suspicious Salary Offer", subtitle: "Posted by Employer XYZ · Flag reason: Unusually high salary", category: "Review", value: "Pending", buttons: [
{ text: "Review", href: "#" },
{ text: "Action", href: "#" },
],
},
{
id: "2", title: "User Report: Inappropriate Profile", subtitle: "Report from user about profile content violation", category: "Report", value: "2 days ago", buttons: [
{ text: "Investigate", href: "#" },
{ text: "Resolve", href: "#" },
],
},
{
id: "3", title: "Automated Alert: Spam Job Posting", subtitle: "System detected duplicate job posts from same employer", category: "Alert", value: "Flagged", buttons: [
{ text: "Review", href: "#" },
{ text: "Remove", href: "#" },
],
},
]}
/>
</div>
<div id="footer" data-section="footer">
<FooterBase
logoText="Jobee Admin"
copyrightText="© 2025 Jobee Admin Portal"
columns={footerColumns}
/>
</div>
</ThemeProvider>
);
}

View File

@@ -0,0 +1,299 @@
"use client";
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
import NavbarStyleCentered from "@/components/navbar/NavbarStyleCentered/NavbarStyleCentered";
import FeatureCardEight from "@/components/sections/feature/FeatureCardEight";
import CardStack from "@/components/cardStack/CardStack";
import ContactCenter from "@/components/sections/contact/ContactCenter";
import FooterBase from "@/components/sections/footer/FooterBase";
import { CheckCircle, Clock, AlertCircle, Mail } from "lucide-react";
import { useState } from "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: "Applications", id: "/applications" },
{ 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: "#" },
],
},
];
interface Application {
id: string;
jobTitle: string;
company: string;
status: "pending" | "accepted" | "rejected" | "interviewing";
appliedDate: string;
lastUpdated: string;
description: string;
}
export default function ApplicationsPage() {
const [applications] = useState<Application[]>([
{
id: "1", jobTitle: "Senior Frontend Developer", company: "Tech Innovations Amsterdam", status: "interviewing", appliedDate: "2025-01-10", lastUpdated: "2025-01-15", description:
"You've been selected for the interview round. Check your email for scheduling details and prepare for a technical assessment."},
{
id: "2", jobTitle: "UX/UI Designer", company: "Creative Studio Rotterdam", status: "accepted", appliedDate: "2025-01-08", lastUpdated: "2025-01-16", description:
"Congratulations! Your application has been accepted. Please review the job offer details and respond within 5 business days."},
{
id: "3", jobTitle: "Data Analyst", company: "Analytics Corp Utrecht", status: "pending", appliedDate: "2025-01-12", lastUpdated: "2025-01-12", description:
"Your application is being reviewed by the hiring team. You'll receive an update within 7-10 business days."},
{
id: "4", jobTitle: "Marketing Manager", company: "Brand Solutions Hague", status: "rejected", appliedDate: "2025-01-05", lastUpdated: "2025-01-14", description:
"Thank you for your application. We've decided to move forward with other candidates but encourage you to apply for future opportunities."},
{
id: "5", jobTitle: "Full Stack Developer", company: "Web Tech Solutions", status: "interviewing", appliedDate: "2025-01-11", lastUpdated: "2025-01-16", description:
"First round interview scheduled for January 20, 2025 at 14:00 CET. Confirmation link sent to your email."},
{
id: "6", jobTitle: "Content Writer", company: "Digital Media Groningen", status: "pending", appliedDate: "2025-01-13", lastUpdated: "2025-01-13", description:
"Your portfolio and writing samples are being evaluated. We'll contact you soon with next steps."},
]);
const getStatusIcon = (status: string) => {
switch (status) {
case "accepted":
return CheckCircle;
case "interviewing":
return Clock;
case "rejected":
return AlertCircle;
default:
return Clock;
}
};
const getStatusColor = (status: string): string => {
switch (status) {
case "accepted":
return "success";
case "interviewing":
return "info";
case "rejected":
return "error";
default:
return "warning";
}
};
const applicationCards = applications.map((app) => (
<div
key={app.id}
className="p-6 rounded-lg border border-border bg-card hover:shadow-lg transition-shadow"
>
<div className="flex items-start justify-between mb-4">
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground mb-1">
{app.jobTitle}
</h3>
<p className="text-sm text-foreground/70 mb-3">{app.company}</p>
</div>
<div className="flex items-center gap-2">
{getStatusIcon(app.status) &&
(() => {
const Icon = getStatusIcon(app.status);
return (
<Icon
className="w-5 h-5"
style={{
color:
app.status === "accepted"
? "#10b981"
: app.status === "rejected"
? "#ef4444"
: app.status === "interviewing"
? "#3b82f6"
: "#f59e0b"}}
/>
);
})()}
<span
className={`px-3 py-1 rounded-full text-xs font-medium capitalize ${
app.status === "accepted"
? "bg-green-100 text-green-800"
: app.status === "rejected"
? "bg-red-100 text-red-800"
: app.status === "interviewing"
? "bg-blue-100 text-blue-800"
: "bg-amber-100 text-amber-800"
}`}
>
{app.status}
</span>
</div>
</div>
<p className="text-sm text-foreground/60 mb-4">{app.description}</p>
<div className="flex justify-between text-xs text-foreground/50 pt-4 border-t border-border/30">
<span>Applied: {new Date(app.appliedDate).toLocaleDateString()}</span>
<span>Updated: {new Date(app.lastUpdated).toLocaleDateString()}</span>
</div>
</div>
));
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 id="hero" data-section="hero" className="py-20">
<FeatureCardEight
title="Your Job Applications"
description="Track the status of all your job applications, monitor interview schedules, and manage your career journey in one place."
tag="Application Tracker"
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
features={[
{
id: 1,
title: "Real-Time Status Updates", description:
"Get instant notifications when employers review or respond to your applications. Never miss an important update.", imageSrc:
"http://img.b2bpic.net/free-photo/corporate-workers-brainstorming-together_23-2148804568.jpg?_wi=1", imageAlt: "Real-time notifications"},
{
id: 2,
title: "Interview Management", description:
"Keep track of interview dates, times, and preparation materials. Get reminders so you never miss an interview opportunity.", imageSrc:
"http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=1", imageAlt: "Interview scheduler"},
{
id: 3,
title: "Application Analytics", description:
"View detailed insights about your application success rate, response times, and which job types get the most attention.", imageSrc:
"http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=1", imageAlt: "Analytics dashboard"},
{
id: 4,
title: "Personalized Recommendations", description:
"Based on your applications and preferences, get tailored job suggestions that match your skills and career goals.", imageSrc:
"http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=1", imageAlt: "Personalized recommendations"},
]}
buttons={[
{
text: "Browse More Jobs", href: "/search"},
]}
buttonAnimation="slide-up"
/>
</div>
<div id="applications" data-section="applications" className="py-20">
<CardStack
title="Your Active Applications"
description="Manage and track all your job applications in one convenient dashboard. Click on any application to view details and take action."
tag="Application Management"
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="uniform-all-items-equal"
mode="buttons"
carouselThreshold={5}
>
{applicationCards}
</CardStack>
</div>
<div id="stats" data-section="stats" className="py-20">
<FeatureCardEight
title="Application Statistics"
description="Monitor your application metrics and track your job search progress over time."
tag="Your Progress"
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={true}
features={[
{
id: 1,
title: "Total Applications", description: `You've submitted ${applications.length} applications across various positions and companies in the Netherlands.`,
imageSrc:
"http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=1", imageAlt: "Total applications"},
{
id: 2,
title: "Response Rate", description: `${Math.round((applications.filter((a) => a.status !== "pending").length / applications.length) * 100)}% of your applications have received responses from employers.`,
imageSrc:
"http://img.b2bpic.net/free-photo/corporate-workers-brainstorming-together_23-2148804568.jpg?_wi=1", imageAlt: "Response rate"},
{
id: 3,
title: "Interviews Scheduled", description: `You have ${applications.filter((a) => a.status === "interviewing").length} interviews currently in the process or scheduled. Prepare and shine!",`,
imageSrc:
"http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=1", imageAlt: "Interviews"},
{
id: 4,
title: "Success Stories", description: `Congratulations! You have ${applications.filter((a) => a.status === "accepted").length} job offers. Review and respond to secure your next opportunity!`,
imageSrc:
"http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=1", imageAlt: "Accepted offers"},
]}
buttons={[
{
text: "Continue Searching", href: "/search"},
]}
buttonAnimation="slide-up"
/>
</div>
<div id="contact" data-section="contact">
<ContactCenter
tag="Need Help?"
title="Stay in Touch with Your Job Search"
description="Get personalized job recommendations and career advice delivered to your inbox. Subscribe to stay updated on new opportunities."
tagIcon={Mail}
tagAnimation="slide-up"
background={{
variant: "animated-grid"}}
useInvertedBackground={false}
inputPlaceholder="Enter your email address"
buttonText="Subscribe"
termsText="We'll send you job recommendations and updates relevant to your applications. Unsubscribe anytime."
/>
</div>
<div id="footer" data-section="footer">
<FooterBase
logoText="Jobee"
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
columns={footerColumns}
/>
</div>
</ThemeProvider>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,7 @@ import { Briefcase, Sparkles, Mail, Quote } from "lucide-react";
const navItems = [
{ name: "Search Jobs", id: "search" },
{ name: "Post a Job", id: "post-job" },
{ name: "Admin", id: "admin-login" },
{ name: "Admin", id: "/admin" },
{ name: "Browse", id: "browse" },
{ name: "Contact", id: "contact" },
];
@@ -211,4 +211,4 @@ export default function HomePage() {
</div>
</ThemeProvider>
);
}
}

View File

@@ -1,48 +1,130 @@
"use client";
import { useState } 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 Link from "next/link";
import { Briefcase, Mail, MapPin, Sparkles } from "lucide-react";
import Input from "@/components/form/Input";
import { Upload, CheckCircle, X } 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: "#" },
],
},
];
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() {
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 [formData, setFormData] = useState<FormData>({
jobTitle: "", companyName: "", location: "", jobType: "Full-time", salary: "", description: "", requirements: "", benefits: "", contactEmail: ""});
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 [errors, setErrors] = useState<FormErrors>({});
const [submitted, setSubmitted] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const validateForm = (): boolean => {
const newErrors: FormErrors = {};
if (!formData.jobTitle.trim()) {
newErrors.jobTitle = "Job title is required";
}
if (!formData.companyName.trim()) {
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) => {
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();
if (!validateForm()) return;
setIsLoading(true);
try {
await new Promise((resolve) => setTimeout(resolve, 2000));
setSubmitted(true);
setFormData({
jobTitle: "", companyName: "", location: "", jobType: "Full-time", salary: "", description: "", requirements: "", benefits: "", contactEmail: ""});
setTimeout(() => setSubmitted(false), 5000);
} catch (error) {
console.error("Submission error:", error);
} finally {
setIsLoading(false);
}
};
return (
<ThemeProvider
@@ -59,65 +141,230 @@ export default function PostJobPage() {
>
<div id="nav" data-section="nav">
<NavbarStyleCentered
navItems={navItems}
button={{ text: "Post a Job", href: "/post-job" }}
brandName="Jobee"
navItems={navItems}
button={{
text: "Post a Job", href: "/post-job"}}
/>
</div>
<div id="features" data-section="features">
<FeatureCardEight
features={[
{
id: 1,
title: "Create a Job Posting", description:
"Fill in job details, requirements, and salary information. Our intuitive form guides you through every step to create a compelling job listing.", imageSrc:
"http://img.b2bpic.net/free-vector/professional-recruitment-plan-diversity-general-infographic-template_23-2148947635.jpg?_wi=4", imageAlt: "Job posting creation interface"},
{
id: 2,
title: "Reach Qualified Candidates", description:
"Your job posting is automatically distributed across our network of active job seekers across all 12 Dutch provinces.", imageSrc:
"http://img.b2bpic.net/free-photo/homepage-concept-with-search-bar_23-2150040187.jpg?_wi=5", imageAlt: "Candidate reach visualization"},
{
id: 3,
title: "Review Applications", description:
"Manage all incoming applications in one centralized dashboard. Review resumes, cover letters, and candidate profiles easily.", imageSrc:
"http://img.b2bpic.net/free-photo/personal-information-form-identity-concept_53876-137622.jpg?_wi=4", imageAlt: "Application review dashboard"},
]}
title="Post a Job in Three Easy Steps"
description="Reach thousands of qualified job seekers across the Netherlands and build your dream team."
tag="Simple Process"
tagIcon={Sparkles}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
buttons={[{ text: "Start Posting", href: "/post-job" }]}
buttonAnimation="slide-up"
/>
</div>
<div className="min-h-screen bg-background py-16 md:py-24">
<div className="mx-auto w-full max-w-2xl px-4 md:px-8">
{/* Header */}
<div className="mb-12 text-center">
<h1 className="mb-4 text-4xl md:text-5xl font-bold text-foreground">
Post a Job
</h1>
<p className="text-lg text-foreground/75">
Reach thousands of qualified job seekers across the Netherlands
</p>
</div>
<div id="contact" data-section="contact">
<ContactCenter
tag="Newsletter"
title="Get Notified About Top Talent"
description="Subscribe to receive alerts when qualified candidates match your job requirements. Stay ahead of the competition and hire the best talent."
tagIcon={Mail}
tagAnimation="slide-up"
background={{ variant: "animated-grid" }}
useInvertedBackground={false}
inputPlaceholder="Enter your company email"
buttonText="Subscribe"
termsText="We respect your privacy. Unsubscribe anytime from our notifications."
/>
{/* Success Message */}
{submitted && (
<div className="mb-8 flex items-center gap-3 rounded-lg bg-green-50 p-4 text-green-700 border border-green-200">
<CheckCircle size={20} />
<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>
)}
{/* Form */}
<form
onSubmit={handleSubmit}
className="rounded-xl bg-card p-8 md:p-12 border border-accent/20 shadow-lg"
>
{/* Job Title */}
<div className="mb-6">
<label className="block mb-2 text-sm font-semibold text-foreground">
Job Title *
</label>
<Input
value={formData.jobTitle}
onChange={(value) => handleInputChange("jobTitle", value)}
placeholder="e.g., Senior Software Engineer"
required
/>
{errors.jobTitle && (
<p className="mt-1 text-sm text-red-600">{errors.jobTitle}</p>
)}
</div>
{/* Company Name */}
<div className="mb-6">
<label className="block mb-2 text-sm font-semibold text-foreground">
Company Name *
</label>
<Input
value={formData.companyName}
onChange={(value) => handleInputChange("companyName", value)}
placeholder="Your company name"
required
/>
{errors.companyName && (
<p className="mt-1 text-sm text-red-600">{errors.companyName}</p>
)}
</div>
{/* Location */}
<div className="mb-6">
<label className="block mb-2 text-sm font-semibold text-foreground">
Location *
</label>
<Input
value={formData.location}
onChange={(value) => handleInputChange("location", value)}
placeholder="e.g., Amsterdam, Netherlands"
required
/>
{errors.location && (
<p className="mt-1 text-sm text-red-600">{errors.location}</p>
)}
</div>
{/* Job Type and Salary */}
<div className="mb-6 grid gap-6 md:grid-cols-2">
<div>
<label className="block mb-2 text-sm font-semibold text-foreground">
Job Type *
</label>
<select
value={formData.jobType}
onChange={(e) => handleInputChange("jobType", e.target.value)}
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"
>
<option>Full-time</option>
<option>Part-time</option>
<option>Contract</option>
<option>Temporary</option>
<option>Internship</option>
</select>
</div>
<div>
<label className="block mb-2 text-sm font-semibold text-foreground">
Salary Range *
</label>
<Input
value={formData.salary}
onChange={(value) => handleInputChange("salary", value)}
placeholder="e.g., €50,000 - €80,000"
required
/>
{errors.salary && (
<p className="mt-1 text-sm text-red-600">{errors.salary}</p>
)}
</div>
</div>
{/* Job Description */}
<div className="mb-6">
<label className="block mb-2 text-sm font-semibold text-foreground">
Job Description *
</label>
<textarea
value={formData.description}
onChange={(e) => handleInputChange("description", e.target.value)}
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
/>
{errors.description && (
<p className="mt-1 text-sm text-red-600">{errors.description}</p>
)}
</div>
{/* Requirements */}
<div className="mb-6">
<label className="block mb-2 text-sm font-semibold text-foreground">
Requirements *
</label>
<textarea
value={formData.requirements}
onChange={(e) => handleInputChange("requirements", e.target.value)}
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
/>
{errors.requirements && (
<p className="mt-1 text-sm text-red-600">{errors.requirements}</p>
)}
</div>
{/* Benefits */}
<div className="mb-6">
<label className="block mb-2 text-sm font-semibold text-foreground">
Benefits (Optional)
</label>
<textarea
value={formData.benefits}
onChange={(e) => handleInputChange("benefits", e.target.value)}
placeholder="Highlight benefits such as remote work, flexible hours, health insurance, training, etc."
rows={4}
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"
/>
</div>
{/* Contact Email */}
<div className="mb-8">
<label className="block mb-2 text-sm font-semibold text-foreground">
Contact Email *
</label>
<Input
value={formData.contactEmail}
onChange={(value) => handleInputChange("contactEmail", value)}
type="email"
placeholder="your-email@company.com"
required
/>
{errors.contactEmail && (
<p className="mt-1 text-sm text-red-600">{errors.contactEmail}</p>
)}
</div>
{/* Submit Button */}
<button
type="submit"
disabled={isLoading}
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"
>
{isLoading ? (
<>
<div className="h-5 w-5 animate-spin rounded-full border-2 border-white border-t-transparent"></div>
Posting...
</>
) : (
<>
<Upload size={18} />
Post Job
</>
)}
</button>
{/* 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>
</div>
</div>
<div id="footer" data-section="footer">
<FooterBase
columns={footerColumns}
logoText="Jobee"
copyrightText="© 2025 Jobee | Dutch Job Listing Platform"
columns={footerColumns}
/>
</div>
</ThemeProvider>
);
}
}

View File

@@ -1,14 +1,13 @@
"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, MapPin, DollarSign, Briefcase, ChevronLeft, ChevronRight, X } from "lucide-react";
const navItems = [
{ name: "Search Jobs", id: "search" },
{ name: "Search Jobs", id: "/search" },
{ name: "Post a Job", id: "post-job" },
{ name: "Admin", id: "admin-login" },
{ name: "Browse", id: "browse" },
@@ -42,7 +41,94 @@ const footerColumns = [
},
];
interface Job {
id: string;
title: string;
company: string;
location: string;
province: string;
salary: string;
type: string;
category: string;
description: string;
posted: string;
}
const mockJobs: Job[] = [
{
id: "1", title: "Senior Software Engineer", company: "TechFlow Solutions", location: "Amsterdam", province: "North Holland", salary: "€80,000 - €120,000", type: "Full-time", category: "Technology", description: "We are looking for an experienced software engineer to join our growing team.", posted: "2 days ago"},
{
id: "2", title: "UX/UI Designer", company: "Creative Studio", location: "Rotterdam", province: "South Holland", salary: "€50,000 - €75,000", type: "Full-time", category: "Design", description: "Join our design team to create beautiful and intuitive user experiences.", posted: "1 day ago"},
{
id: "3", title: "Marketing Manager", company: "Global Marketing Inc", location: "Utrecht", province: "Utrecht", salary: "€60,000 - €85,000", type: "Full-time", category: "Marketing", description: "Lead our marketing initiatives and strategy for European markets.", posted: "3 days ago"},
{
id: "4", title: "Data Analyst", company: "DataViz Corp", location: "Amsterdam", province: "North Holland", salary: "€55,000 - €80,000", type: "Full-time", category: "Technology", description: "Analyze complex datasets and create actionable insights for stakeholders.", posted: "1 week ago"},
{
id: "5", title: "Sales Representative", company: "SalesForce Pro", location: "Groningen", province: "Groningen", salary: "€45,000 - €65,000", type: "Full-time", category: "Sales", description: "Build relationships with clients and close deals in the B2B tech sector.", posted: "4 days ago"},
{
id: "6", title: "Product Manager", company: "Innovation Labs", location: "Amsterdam", province: "North Holland", salary: "€75,000 - €110,000", type: "Full-time", category: "Technology", description: "Define product strategy and lead cross-functional teams.", posted: "5 days ago"},
{
id: "7", title: "HR Specialist", company: "People First", location: "The Hague", province: "South Holland", salary: "€40,000 - €60,000", type: "Part-time", category: "Human Resources", description: "Support recruitment, onboarding, and employee development.", posted: "6 days ago"},
{
id: "8", title: "Backend Developer", company: "Cloud Systems", location: "Amsterdam", province: "North Holland", salary: "€70,000 - €100,000", type: "Full-time", category: "Technology", description: "Develop scalable backend systems and APIs using modern technologies.", posted: "2 weeks ago"},
{
id: "9", title: "Content Writer", company: "Content Creators", location: "Utrecht", province: "Utrecht", salary: "€35,000 - €50,000", type: "Full-time", category: "Marketing", description: "Create engaging content for blogs, social media, and marketing campaigns.", posted: "3 days ago"},
{
id: "10", title: "DevOps Engineer", company: "Infrastructure Pro", location: "Amsterdam", province: "North Holland", salary: "€65,000 - €95,000", type: "Full-time", category: "Technology", description: "Maintain and optimize cloud infrastructure and CI/CD pipelines.", posted: "1 week ago"},
{
id: "11", title: "Financial Analyst", company: "Finance Solutions", location: "Amsterdam", province: "North Holland", salary: "€55,000 - €80,000", type: "Full-time", category: "Finance", description: "Analyze financial data and provide insights for investment decisions.", posted: "4 days ago"},
{
id: "12", title: "Customer Support Manager", company: "Support Hub", location: "Leiden", province: "South Holland", salary: "€45,000 - €65,000", type: "Full-time", category: "Customer Service", description: "Lead and manage customer support team to ensure excellent service quality.", posted: "5 days ago"},
];
const provinces = [
"North Holland", "South Holland", "Utrecht", "Groningen", "The Hague", "Friesland", "Drenthe", "Flevoland", "Overijssel", "Gelderland", "Limburg", "North Brabant"];
const categories = ["Technology", "Design", "Marketing", "Sales", "Human Resources", "Finance", "Customer Service"];
const jobTypes = ["Full-time", "Part-time", "Contract", "Freelance"];
export default function SearchPage() {
const [searchQuery, setSearchQuery] = useState("");
const [selectedProvinces, setSelectedProvinces] = useState<string[]>([]);
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const [selectedTypes, setSelectedTypes] = useState<string[]>([]);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 6;
const filteredJobs = useMemo(() => {
return mockJobs.filter((job) => {
const matchesQuery =
searchQuery === "" ||
job.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
job.company.toLowerCase().includes(searchQuery.toLowerCase()) ||
job.description.toLowerCase().includes(searchQuery.toLowerCase());
const matchesProvince =
selectedProvinces.length === 0 || selectedProvinces.includes(job.province);
const matchesCategory =
selectedCategories.length === 0 || selectedCategories.includes(job.category);
const matchesType = selectedTypes.length === 0 || selectedTypes.includes(job.type);
return matchesQuery && matchesProvince && matchesCategory && matchesType;
});
}, [searchQuery, selectedProvinces, selectedCategories, selectedTypes]);
const totalPages = Math.ceil(filteredJobs.length / itemsPerPage);
const paginatedJobs = filteredJobs.slice(
(currentPage - 1) * itemsPerPage,
currentPage * itemsPerPage
);
const toggleFilter = (value: string, setter: Function, state: string[]) => {
if (state.includes(value)) {
setter(state.filter((item) => item !== value));
} else {
setter([...state, value]);
}
};
return (
<ThemeProvider
defaultButtonVariant="text-stagger"
@@ -65,54 +151,241 @@ 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-br from-slate-50 to-slate-100 dark:from-slate-950 dark:to-slate-900 pt-20 pb-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Search Header */}
<div className="mb-12">
<h1 className="text-4xl sm:text-5xl font-bold text-slate-900 dark:text-white mb-4">
Find Your Dream Job
</h1>
<p className="text-lg text-slate-600 dark:text-slate-300 mb-8">
Browse thousands of opportunities across all Dutch provinces
</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."
/>
{/* Search Bar */}
<div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-slate-400 w-5 h-5" />
<input
type="text"
placeholder="Search by job title, company, or keyword..."
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
setCurrentPage(1);
}}
className="w-full pl-12 pr-4 py-3 rounded-lg border border-slate-200 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-4 gap-8">
{/* Filters Sidebar */}
<div className="lg:col-span-1">
<div className="sticky top-24 space-y-6 bg-white dark:bg-slate-800 p-6 rounded-lg border border-slate-200 dark:border-slate-700">
<div>
<h3 className="font-semibold text-slate-900 dark:text-white mb-3 text-sm uppercase tracking-wide">
Provinces
</h3>
<div className="space-y-2 max-h-64 overflow-y-auto">
{provinces.map((province) => (
<label key={province} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={selectedProvinces.includes(province)}
onChange={() =>
toggleFilter(province, setSelectedProvinces, selectedProvinces)
}
className="w-4 h-4 rounded border-slate-300 text-blue-600 focus:ring-2 focus:ring-blue-500"
/>
<span className="text-sm text-slate-700 dark:text-slate-300">
{province}
</span>
</label>
))}
</div>
</div>
<div className="border-t border-slate-200 dark:border-slate-700 pt-4">
<h3 className="font-semibold text-slate-900 dark:text-white mb-3 text-sm uppercase tracking-wide">
Category
</h3>
<div className="space-y-2">
{categories.map((category) => (
<label key={category} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={selectedCategories.includes(category)}
onChange={() =>
toggleFilter(category, setSelectedCategories, selectedCategories)
}
className="w-4 h-4 rounded border-slate-300 text-blue-600 focus:ring-2 focus:ring-blue-500"
/>
<span className="text-sm text-slate-700 dark:text-slate-300">
{category}
</span>
</label>
))}
</div>
</div>
<div className="border-t border-slate-200 dark:border-slate-700 pt-4">
<h3 className="font-semibold text-slate-900 dark:text-white mb-3 text-sm uppercase tracking-wide">
Job Type
</h3>
<div className="space-y-2">
{jobTypes.map((type) => (
<label key={type} className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={selectedTypes.includes(type)}
onChange={() => toggleFilter(type, setSelectedTypes, selectedTypes)}
className="w-4 h-4 rounded border-slate-300 text-blue-600 focus:ring-2 focus:ring-blue-500"
/>
<span className="text-sm text-slate-700 dark:text-slate-300">
{type}
</span>
</label>
))}
</div>
</div>
{/* Clear Filters */}
{(selectedProvinces.length > 0 ||
selectedCategories.length > 0 ||
selectedTypes.length > 0) && (
<button
onClick={() => {
setSelectedProvinces([]);
setSelectedCategories([]);
setSelectedTypes([]);
setCurrentPage(1);
}}
className="w-full py-2 px-3 bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-lg text-sm font-medium hover:bg-slate-200 dark:hover:bg-slate-600 transition-colors"
>
Clear Filters
</button>
)}
</div>
</div>
{/* Job Listings */}
<div className="lg:col-span-3">
{/* Results Count */}
<div className="mb-6 flex items-center justify-between">
<p className="text-sm text-slate-600 dark:text-slate-400">
Showing {paginatedJobs.length > 0 ? (currentPage - 1) * itemsPerPage + 1 : 0} to{" "}
{Math.min(currentPage * itemsPerPage, filteredJobs.length)} of{" "}
<span className="font-semibold text-slate-900 dark:text-white">
{filteredJobs.length}
</span>{" "}
jobs
</p>
</div>
{/* Job Cards Grid */}
{paginatedJobs.length > 0 ? (
<div className="grid gap-4 mb-8">
{paginatedJobs.map((job) => (
<div
key={job.id}
className="bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg p-6 hover:shadow-lg hover:border-blue-300 dark:hover:border-blue-600 transition-all duration-300 cursor-pointer group"
>
<div className="flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4">
<div className="flex-1">
<div className="flex items-start gap-3 mb-3">
<div className="flex-1">
<h3 className="text-lg font-semibold text-slate-900 dark:text-white group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors">
{job.title}
</h3>
<p className="text-sm text-slate-600 dark:text-slate-400 mt-1">
{job.company}
</p>
</div>
<span className="px-3 py-1 bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 text-xs font-medium rounded-full whitespace-nowrap">
{job.type}
</span>
</div>
<p className="text-sm text-slate-600 dark:text-slate-400 mb-4 line-clamp-2">
{job.description}
</p>
<div className="flex flex-wrap gap-3 text-sm">
<div className="flex items-center gap-1 text-slate-600 dark:text-slate-400">
<MapPin className="w-4 h-4" />
<span>{job.location}</span>
</div>
<div className="flex items-center gap-1 text-slate-600 dark:text-slate-400">
<DollarSign className="w-4 h-4" />
<span>{job.salary}</span>
</div>
<div className="flex items-center gap-1 text-slate-600 dark:text-slate-400">
<Briefcase className="w-4 h-4" />
<span>{job.category}</span>
</div>
</div>
</div>
<div className="text-right text-xs text-slate-500 dark:text-slate-500 whitespace-nowrap">
<p>{job.posted}</p>
<button className="mt-2 px-4 py-2 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
Apply
</button>
</div>
</div>
</div>
))}
</div>
) : (
<div className="text-center py-12">
<p className="text-slate-600 dark:text-slate-400 mb-2">
No jobs found matching your criteria.
</p>
<p className="text-sm text-slate-500 dark:text-slate-500">
Try adjusting your filters or search query.
</p>
</div>
)}
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-2">
<button
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))}
disabled={currentPage === 1}
className="p-2 rounded-lg border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<ChevronLeft className="w-5 h-5" />
</button>
<div className="flex items-center gap-1">
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => (
<button
key={page}
onClick={() => setCurrentPage(page)}
className={`w-10 h-10 rounded-lg font-medium transition-colors ${
currentPage === page
? "bg-blue-600 text-white"
: "border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700"
}`}
>
{page}
</button>
))}
</div>
<button
onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))}
disabled={currentPage === totalPages}
className="p-2 rounded-lg border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
<ChevronRight className="w-5 h-5" />
</button>
</div>
)}
</div>
</div>
</div>
</div>
<div id="footer" data-section="footer">
@@ -124,4 +397,4 @@ export default function SearchPage() {
</div>
</ThemeProvider>
);
}
}