234 lines
15 KiB
JavaScript
234 lines
15 KiB
JavaScript
import React, { useState, useMemo } from 'react';
|
|
import { Head, Link } from '@inertiajs/react';
|
|
import EmployerLayout from '@/Layouts/EmployerLayout';
|
|
import {
|
|
Plus,
|
|
Search,
|
|
Filter,
|
|
Download,
|
|
MoreHorizontal,
|
|
MapPin,
|
|
DollarSign,
|
|
Users,
|
|
Calendar,
|
|
Briefcase,
|
|
CheckCircle2,
|
|
XCircle,
|
|
Clock,
|
|
Eye,
|
|
Edit2
|
|
} from 'lucide-react';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
|
|
export default function Index({ initialJobs }) {
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [statusFilter, setStatusFilter] = useState('All');
|
|
|
|
const [jobs, setJobs] = useState(initialJobs || [
|
|
{ id: 1, title: 'Experienced Mason', category: 'Mason', location: 'Dubai Marina', salary: 2800, workers_needed: 5, applied_count: 3, posted_at: 'Nov 12, 2026', status: 'Active' },
|
|
{ id: 2, title: 'Villa Cleaner', category: 'Cleaner', location: 'Al Barsha', salary: 1800, workers_needed: 2, applied_count: 12, posted_at: 'Nov 10, 2026', status: 'Active' },
|
|
{ id: 3, title: 'Site Supervisor', category: 'Site Supervisor', location: 'Sharjah', salary: 4500, workers_needed: 1, applied_count: 8, posted_at: 'Nov 05, 2026', status: 'Closed' },
|
|
{ id: 4, title: 'Plumber for Project', category: 'Plumber', location: 'Jumeirah', salary: 2600, workers_needed: 3, applied_count: 0, posted_at: 'Nov 14, 2026', status: 'Draft' },
|
|
]);
|
|
|
|
const filteredJobs = useMemo(() => {
|
|
return jobs.filter(job => {
|
|
const matchesSearch = job.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
job.category.toLowerCase().includes(searchTerm.toLowerCase());
|
|
const matchesStatus = statusFilter === 'All' || job.status === statusFilter;
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
}, [jobs, searchTerm, statusFilter]);
|
|
|
|
return (
|
|
<EmployerLayout title="My Jobs">
|
|
<Head title="My Jobs - Employer Portal" />
|
|
|
|
<div className="space-y-8 select-none">
|
|
{/* Header Actions */}
|
|
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-2xl font-black text-slate-900 tracking-tight">Active Job Postings</h1>
|
|
<p className="text-xs font-medium text-slate-500 mt-1">Manage and track your recruitment status</p>
|
|
</div>
|
|
<Link
|
|
href="/employer/jobs/create"
|
|
className="bg-[#185FA5] text-white px-6 py-3 rounded-2xl font-black text-xs uppercase tracking-widest shadow-xl shadow-blue-500/20 hover:bg-[#144f8a] transition-all flex items-center justify-center space-x-2"
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
<span>Post a Job</span>
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Filters & Search Bar */}
|
|
<div className="bg-white p-6 rounded-3xl border border-slate-200 shadow-sm flex flex-col md:flex-row gap-4">
|
|
<div className="relative flex-1">
|
|
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
|
|
<input
|
|
type="text"
|
|
placeholder="Search by job title or category..."
|
|
className="w-full pl-11 pr-4 py-3 bg-slate-50 border border-slate-100 rounded-2xl text-sm font-bold text-slate-900 focus:ring-4 focus:ring-blue-500/10 focus:border-[#185FA5] outline-none transition-all"
|
|
value={searchTerm}
|
|
onChange={e => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-3">
|
|
<div className="flex items-center bg-slate-50 border border-slate-100 p-1.5 rounded-2xl">
|
|
{['All', 'Active', 'Closed', 'Draft'].map(status => (
|
|
<button
|
|
key={status}
|
|
onClick={() => setStatusFilter(status)}
|
|
className={`px-4 py-2 text-[10px] font-black rounded-xl transition-all ${
|
|
statusFilter === status
|
|
? 'bg-white text-[#185FA5] shadow-sm border border-slate-200/50'
|
|
: 'text-slate-400 hover:text-slate-600'
|
|
}`}
|
|
>
|
|
{status.toUpperCase()}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<button className="p-3 bg-white border border-slate-200 text-slate-400 hover:text-[#185FA5] rounded-xl transition-all shadow-sm">
|
|
<Download className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Jobs Grid */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
{filteredJobs.length > 0 ? (
|
|
filteredJobs.map((job) => (
|
|
<div key={job.id} className="bg-white rounded-[32px] border border-slate-200 shadow-sm hover:shadow-md transition-all overflow-hidden group">
|
|
<div className="p-8 space-y-6">
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex items-start space-x-4">
|
|
<div className="w-12 h-12 bg-blue-50 rounded-2xl flex items-center justify-center flex-shrink-0">
|
|
<Briefcase className="w-6 h-6 text-[#185FA5]" />
|
|
</div>
|
|
<div>
|
|
<h3 className="font-black text-slate-900 tracking-tight group-hover:text-[#185FA5] transition-colors line-clamp-1">{job.title}</h3>
|
|
<div className="flex items-center space-x-2 mt-1">
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">{job.category}</span>
|
|
<span className="w-1 h-1 bg-slate-300 rounded-full" />
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">{job.posted_at}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Badge
|
|
variant="outline"
|
|
className={`px-3 py-1 rounded-full text-[10px] font-black uppercase tracking-widest ${
|
|
job.status === 'Active' ? 'bg-emerald-50 text-emerald-700 border-emerald-100' :
|
|
job.status === 'Closed' ? 'bg-rose-50 text-rose-700 border-rose-100' :
|
|
'bg-slate-50 text-slate-500 border-slate-100'
|
|
}`}
|
|
>
|
|
{job.status === 'Active' && <CheckCircle2 className="w-3 h-3 mr-1" />}
|
|
{job.status === 'Closed' && <XCircle className="w-3 h-3 mr-1" />}
|
|
{job.status === 'Draft' && <Clock className="w-3 h-3 mr-1" />}
|
|
{job.status}
|
|
</Badge>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-4 py-4 border-y border-slate-50">
|
|
<div className="space-y-1">
|
|
<div className="text-[9px] font-black text-slate-400 uppercase tracking-[0.15em]">Location</div>
|
|
<div className="flex items-center space-x-1.5 text-xs font-bold text-slate-700">
|
|
<MapPin className="w-3.5 h-3.5 text-slate-400" />
|
|
<span>{job.location}</span>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<div className="text-[9px] font-black text-slate-400 uppercase tracking-[0.15em]">Monthly Salary</div>
|
|
<div className="flex items-center space-x-1.5 text-xs font-bold text-slate-700">
|
|
<DollarSign className="w-3.5 h-3.5 text-emerald-600" />
|
|
<span>{job.salary} AED</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-between pt-2">
|
|
<div className="flex items-center space-x-3">
|
|
<div className="flex -space-x-2">
|
|
{[...Array(3)].map((_, i) => (
|
|
<div key={i} className="w-6 h-6 rounded-full border-2 border-white bg-slate-100 flex items-center justify-center text-[8px] font-bold text-slate-400">
|
|
{i === 2 ? `+${job.applied_count}` : ''}
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="text-[10px] font-black text-slate-600 uppercase tracking-tight">
|
|
{job.applied_count} <span className="text-slate-400">Applications</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-[10px] font-black text-slate-600 uppercase tracking-tight">
|
|
{job.applied_count < job.workers_needed ? (
|
|
<span>{job.applied_count}/{job.workers_needed} <span className="text-slate-400">Filled</span></span>
|
|
) : (
|
|
<span className="text-emerald-600 flex items-center"><CheckCircle2 className="w-3 h-3 mr-1" /> Fully Staffed</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-3 pt-2">
|
|
<Link
|
|
href={`/employer/jobs/${job.id}/applicants`}
|
|
className="flex-1 bg-[#185FA5] text-white py-3 rounded-xl font-black text-[10px] uppercase tracking-[0.15em] shadow-lg shadow-blue-500/10 hover:bg-[#144f8a] transition-all flex items-center justify-center space-x-2"
|
|
>
|
|
<Eye className="w-3.5 h-3.5" />
|
|
<span>View Applicants</span>
|
|
</Link>
|
|
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<button className="p-3 bg-slate-50 text-slate-400 hover:text-slate-900 rounded-xl transition-all">
|
|
<MoreHorizontal className="w-5 h-5" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-48 p-2 rounded-2xl shadow-xl">
|
|
<DropdownMenuLabel className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-2 py-2">Quick Actions</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem className="p-3 rounded-xl focus:bg-blue-50 cursor-pointer">
|
|
<div className="flex items-center space-x-3 text-[#185FA5]">
|
|
<Edit2 className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Edit Posting</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="p-3 rounded-xl focus:bg-rose-50 cursor-pointer">
|
|
<div className="flex items-center space-x-3 text-rose-600">
|
|
<XCircle className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Close Job</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))
|
|
) : (
|
|
<div className="col-span-full py-20 text-center bg-white rounded-[32px] border border-dashed border-slate-200">
|
|
<Briefcase className="w-12 h-12 text-slate-200 mx-auto mb-4" />
|
|
<h3 className="text-base font-bold text-slate-400 uppercase tracking-widest">No jobs found</h3>
|
|
<Link href="/employer/jobs/create" className="mt-4 inline-flex items-center space-x-2 text-[#185FA5] font-black text-xs uppercase tracking-widest hover:underline">
|
|
<Plus className="w-4 h-4" />
|
|
<span>Post your first job</span>
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</EmployerLayout>
|
|
);
|
|
}
|