525 lines
35 KiB
JavaScript
525 lines
35 KiB
JavaScript
import React, { useState, useMemo } from 'react';
|
|
import { Head, Link, router } from '@inertiajs/react';
|
|
import EmployerLayout from '@/Layouts/EmployerLayout';
|
|
import {
|
|
Plus,
|
|
Search,
|
|
Filter,
|
|
Download,
|
|
MoreHorizontal,
|
|
MapPin,
|
|
DollarSign,
|
|
Users,
|
|
Calendar,
|
|
Briefcase,
|
|
CheckCircle2,
|
|
XCircle,
|
|
Clock,
|
|
Eye,
|
|
Edit2,
|
|
Trash2,
|
|
LayoutGrid,
|
|
List,
|
|
Shield,
|
|
Navigation,
|
|
Layers
|
|
} from 'lucide-react';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { toast } from 'sonner';
|
|
|
|
// Helper function to map jobs dynamically to categories and skills based on title
|
|
const getJobMetadata = (title = '') => {
|
|
const t = title.toLowerCase();
|
|
if (t.includes('electrician') || t.includes('electrical')) {
|
|
return {
|
|
category: 'Skilled Worker',
|
|
skills: ['Electrical Wiring', 'Maintenance', 'Safety Inspection', 'Wiring Diagrams']
|
|
};
|
|
}
|
|
if (t.includes('clean') || t.includes('housekeep') || t.includes('maid') || t.includes('laundry')) {
|
|
return {
|
|
category: 'General Worker',
|
|
skills: ['Cleaning', 'Housekeeping', 'Laundry', 'Sanitization']
|
|
};
|
|
}
|
|
if (t.includes('plumb')) {
|
|
return {
|
|
category: 'Skilled Worker',
|
|
skills: ['Plumbing', 'Pipe Fitting', 'Leak Repair', 'Blueprint Reading']
|
|
};
|
|
}
|
|
if (t.includes('security') || t.includes('guard') || t.includes('watchman')) {
|
|
return {
|
|
category: 'Security',
|
|
skills: ['Security', 'Surveillance', 'Access Control', 'Patrolling']
|
|
};
|
|
}
|
|
if (t.includes('driver') || t.includes('chauffeur') || t.includes('delivery')) {
|
|
return {
|
|
category: 'Driver',
|
|
skills: ['Driving', 'Route Knowledge', 'Vehicle Safety', 'Navigation']
|
|
};
|
|
}
|
|
if (t.includes('mason') || t.includes('carpenter') || t.includes('woodwork') || t.includes('construction')) {
|
|
return {
|
|
category: 'Skilled Worker',
|
|
skills: ['Carpentry', 'Woodwork', 'Masonry', 'Tool Operation']
|
|
};
|
|
}
|
|
if (t.includes('store') || t.includes('warehouse') || t.includes('inventory') || t.includes('keeper')) {
|
|
return {
|
|
category: 'Logistics',
|
|
skills: ['Inventory', 'Stock Management', 'Packaging', 'Labeling']
|
|
};
|
|
}
|
|
if (t.includes('helper') || t.includes('labor') || t.includes('assistant')) {
|
|
return {
|
|
category: 'General Worker',
|
|
skills: ['Loading', 'Unloading', 'Physical Labor', 'Material Handling']
|
|
};
|
|
}
|
|
// Fallback
|
|
return {
|
|
category: 'Skilled Worker',
|
|
skills: ['General Support', 'Task Execution', 'Operations']
|
|
};
|
|
};
|
|
|
|
export default function Index({ initialJobs }) {
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [statusFilter, setStatusFilter] = useState('All');
|
|
const [categoryFilter, setCategoryFilter] = useState('All');
|
|
const [skillFilter, setSkillFilter] = useState('All');
|
|
const [viewMode, setViewMode] = useState('grid');
|
|
const [jobs, setJobs] = useState(initialJobs || []);
|
|
|
|
const handleDelete = (jobId) => {
|
|
if (confirm('Are you sure you want to delete this job posting? This action cannot be undone.')) {
|
|
router.delete(`/employer/jobs/${jobId}`, {
|
|
onSuccess: () => {
|
|
toast.success('Job deleted successfully.');
|
|
},
|
|
onError: () => {
|
|
toast.error('Failed to delete job.');
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
// Derived category list
|
|
const categories = ['All', 'Skilled Worker', 'General Worker', 'Security', 'Driver', 'Logistics'];
|
|
|
|
// Derived skills list
|
|
const skills = [
|
|
'All',
|
|
'Electrical Wiring',
|
|
'Cleaning',
|
|
'Housekeeping',
|
|
'Plumbing',
|
|
'Pipe Fitting',
|
|
'Security',
|
|
'Surveillance',
|
|
'Driving',
|
|
'Route Knowledge',
|
|
'Carpentry',
|
|
'Woodwork',
|
|
'Inventory',
|
|
'Stock Management',
|
|
'Loading',
|
|
'Unloading'
|
|
];
|
|
|
|
const filteredJobs = useMemo(() => {
|
|
return jobs.filter(job => {
|
|
const meta = getJobMetadata(job.title);
|
|
const matchesSearch = job.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
job.location.toLowerCase().includes(searchTerm.toLowerCase());
|
|
|
|
const matchesStatus = statusFilter === 'All' || job.status.toLowerCase() === statusFilter.toLowerCase();
|
|
const matchesCategory = categoryFilter === 'All' || meta.category === categoryFilter;
|
|
const matchesSkill = skillFilter === 'All' || meta.skills.includes(skillFilter);
|
|
|
|
return matchesSearch && matchesStatus && matchesCategory && matchesSkill;
|
|
});
|
|
}, [jobs, searchTerm, statusFilter, categoryFilter, skillFilter]);
|
|
|
|
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-[28px] font-black text-[#0f172a] tracking-tight">Active Job Postings</h1>
|
|
<p className="text-sm 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-7 py-3.5 rounded-[18px] font-extrabold text-xs uppercase tracking-widest shadow-xl shadow-blue-500/10 hover:bg-[#144f8a] transition-all flex items-center justify-center space-x-2"
|
|
>
|
|
<Plus className="w-4 h-4 stroke-[3px]" />
|
|
<span>Post a Job</span>
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Filters & Search Bar */}
|
|
<div className="bg-white p-6 rounded-[28px] border border-slate-200/80 shadow-sm flex flex-col xl:flex-row gap-4 items-stretch xl:items-center">
|
|
{/* Search */}
|
|
<div className="relative flex-1 min-w-[280px]">
|
|
<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, skills, or location..."
|
|
className="w-full pl-11 pr-4 py-3 bg-slate-50/50 border border-slate-200/80 rounded-2xl text-xs font-bold text-slate-800 focus:ring-4 focus:ring-blue-500/5 focus:border-[#185FA5] focus:bg-white outline-none transition-all placeholder-slate-400"
|
|
value={searchTerm}
|
|
onChange={e => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
{/* Select Dropdowns */}
|
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
<select
|
|
className="px-4 py-3 bg-slate-50/50 border border-slate-200/80 rounded-2xl text-xs font-bold text-slate-700 focus:ring-4 focus:ring-blue-500/5 focus:border-[#185FA5] outline-none transition-all min-w-[160px]"
|
|
value={categoryFilter}
|
|
onChange={e => setCategoryFilter(e.target.value)}
|
|
>
|
|
<option value="All">All Categories</option>
|
|
{categories.filter(c => c !== 'All').map(c => (
|
|
<option key={c} value={c}>{c}</option>
|
|
))}
|
|
</select>
|
|
|
|
<select
|
|
className="px-4 py-3 bg-slate-50/50 border border-slate-200/80 rounded-2xl text-xs font-bold text-slate-700 focus:ring-4 focus:ring-blue-500/5 focus:border-[#185FA5] outline-none transition-all min-w-[160px]"
|
|
value={skillFilter}
|
|
onChange={e => setSkillFilter(e.target.value)}
|
|
>
|
|
<option value="All">All Skills</option>
|
|
{skills.filter(s => s !== 'All').map(s => (
|
|
<option key={s} value={s}>{s}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Status Toggle & Views */}
|
|
<div className="flex flex-wrap items-center gap-4 justify-between xl:justify-end flex-shrink-0">
|
|
<div className="flex items-center bg-slate-50 border border-slate-100 p-1 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 className="h-6 w-px bg-slate-200" />
|
|
<button
|
|
onClick={() => setViewMode('grid')}
|
|
className={`p-3 rounded-xl transition-all border ${viewMode === 'grid' ? 'bg-blue-50 border-blue-100 text-[#185FA5]' : 'bg-white border-slate-200 text-slate-400 hover:text-[#185FA5]'}`}
|
|
>
|
|
<LayoutGrid className="w-4 h-4" />
|
|
</button>
|
|
<button
|
|
onClick={() => setViewMode('list')}
|
|
className={`p-3 rounded-xl transition-all border ${viewMode === 'list' ? 'bg-blue-50 border-blue-100 text-[#185FA5]' : 'bg-white border-slate-200 text-slate-400 hover:text-[#185FA5]'}`}
|
|
>
|
|
<List className="w-4 h-4" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Jobs Display */}
|
|
{filteredJobs.length > 0 ? (
|
|
viewMode === 'grid' ? (
|
|
/* Grid Layout */
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{filteredJobs.map((job) => {
|
|
const meta = getJobMetadata(job.title);
|
|
const isFilled = job.hired_count >= job.workers_needed;
|
|
const percentFilled = Math.min(100, Math.round((job.hired_count / job.workers_needed) * 100));
|
|
|
|
return (
|
|
<div key={job.id} className="bg-white rounded-[28px] border border-slate-200/80 shadow-sm hover:shadow-md transition-all duration-300 overflow-hidden flex flex-col group">
|
|
<div className="p-6 flex-1 flex flex-col justify-between space-y-5">
|
|
{/* Header */}
|
|
<div className="flex items-start justify-between gap-2">
|
|
<div className="flex items-start space-x-3.5">
|
|
<div className="w-11 h-11 bg-blue-50/50 rounded-2xl flex items-center justify-center flex-shrink-0 border border-blue-100/50">
|
|
{meta.category === 'Security' ? <Shield className="w-5.5 h-5.5 text-[#185FA5]" /> :
|
|
meta.category === 'Driver' ? <Navigation className="w-5.5 h-5.5 text-[#185FA5]" /> :
|
|
meta.category === 'Logistics' ? <Layers className="w-5.5 h-5.5 text-[#185FA5]" /> :
|
|
<Briefcase className="w-5.5 h-5.5 text-[#185FA5]" />}
|
|
</div>
|
|
<div>
|
|
<h3 className="font-extrabold text-slate-900 text-[14px] leading-snug tracking-tight group-hover:text-[#185FA5] transition-colors line-clamp-2">
|
|
<Link href={`/employer/jobs/${job.id}`}>{job.title}</Link>
|
|
</h3>
|
|
<span className="text-[10px] font-bold text-slate-400 block mt-1 uppercase tracking-wider">{job.posted_at}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<Badge
|
|
variant="outline"
|
|
className={`px-2.5 py-0.5 rounded-full text-[9px] font-black uppercase tracking-wider flex items-center flex-shrink-0 border-0 ${
|
|
job.status.toLowerCase() === 'active' ? 'bg-emerald-50 text-emerald-700' :
|
|
job.status.toLowerCase() === 'closed' ? 'bg-rose-50 text-rose-700' :
|
|
'bg-slate-50 text-slate-500'
|
|
}`}
|
|
>
|
|
<span className={`w-1.5 h-1.5 rounded-full mr-1.5 ${
|
|
job.status.toLowerCase() === 'active' ? 'bg-emerald-500' :
|
|
job.status.toLowerCase() === 'closed' ? 'bg-rose-500' :
|
|
'bg-slate-400'
|
|
}`} />
|
|
{job.status}
|
|
</Badge>
|
|
</div>
|
|
|
|
{/* Location & Salary */}
|
|
<div className="space-y-2 text-xs font-bold text-slate-600">
|
|
<div className="flex items-center space-x-2">
|
|
<MapPin className="w-4 h-4 text-slate-400" />
|
|
<span className="line-clamp-1">{job.location}</span>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<DollarSign className="w-4 h-4 text-emerald-600" />
|
|
<span>$ {job.salary.toLocaleString()} AED / month</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats & Progress Bar */}
|
|
<div className="space-y-2 bg-slate-50/50 p-4 rounded-2xl border border-slate-100">
|
|
<div className="grid grid-cols-3 gap-2 text-center">
|
|
<div>
|
|
<div className="text-[14px] font-black text-slate-900">{job.workers_needed}</div>
|
|
<div className="text-[9px] font-extrabold text-slate-400 uppercase tracking-wider">Total Req</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-[14px] font-black text-slate-900">{job.hired_count}</div>
|
|
<div className="text-[9px] font-extrabold text-slate-400 uppercase tracking-wider">Hired</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-[14px] font-black text-slate-900">{job.applied_count}</div>
|
|
<div className="text-[9px] font-extrabold text-slate-400 uppercase tracking-wider">Applications</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1">
|
|
<div className="w-full h-1.5 bg-slate-100 rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full rounded-full transition-all duration-500 ${isFilled ? 'bg-emerald-500' : 'bg-[#185FA5]'}`}
|
|
style={{ width: `${percentFilled}%` }}
|
|
/>
|
|
</div>
|
|
<div className="flex justify-end text-[9px] font-black uppercase tracking-wider">
|
|
{isFilled ? (
|
|
<span className="text-emerald-600">All positions filled</span>
|
|
) : (
|
|
<span className="text-slate-500">{job.workers_needed - job.hired_count} more to hire</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Skills Tags */}
|
|
<div className="space-y-1">
|
|
<div className="text-[9px] font-extrabold text-slate-400 uppercase tracking-wider">Skills</div>
|
|
<div className="flex flex-wrap gap-1.5">
|
|
{meta.skills.slice(0, 2).map((skill, idx) => (
|
|
<span key={idx} className="bg-slate-50 text-slate-600 border border-slate-100 px-2.5 py-1 rounded-lg text-[10px] font-bold">
|
|
{skill}
|
|
</span>
|
|
))}
|
|
{meta.skills.length > 2 && (
|
|
<span className="bg-blue-50 text-[#185FA5] px-2 py-1 rounded-lg text-[10px] font-black">
|
|
+{meta.skills.length - 2}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Category Row */}
|
|
<div className="flex items-center justify-between text-xs border-t border-slate-100 pt-3.5">
|
|
<span className="font-bold text-slate-400">Category</span>
|
|
<span className="font-extrabold text-slate-700">{meta.category}</span>
|
|
</div>
|
|
|
|
{/* Actions footer */}
|
|
<div className="flex items-center space-x-3 pt-1">
|
|
<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/5 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 hover:bg-slate-100 border border-slate-100 text-slate-400 hover:text-slate-900 rounded-xl transition-all">
|
|
<MoreHorizontal className="w-4 h-4" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-48 p-2 rounded-2xl shadow-xl border border-slate-100">
|
|
<DropdownMenuLabel className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-2 py-2">Quick Actions</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem className="p-2.5 rounded-xl focus:bg-blue-50 cursor-pointer" asChild>
|
|
<Link href={`/employer/jobs/${job.id}`} className="flex items-center space-x-3 text-slate-700 w-full">
|
|
<Eye className="w-4 h-4 text-blue-500" />
|
|
<span className="text-xs font-bold">View Details</span>
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="p-2.5 rounded-xl focus:bg-blue-50 cursor-pointer" asChild>
|
|
<Link href={`/employer/jobs/${job.id}/edit`} className="flex items-center space-x-3 text-[#185FA5] w-full">
|
|
<Edit2 className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Edit Posting</span>
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="p-2.5 rounded-xl focus:bg-rose-50 cursor-pointer" onClick={() => handleDelete(job.id)}>
|
|
<div className="flex items-center space-x-3 text-rose-600 w-full">
|
|
<Trash2 className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Delete Job</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
) : (
|
|
/* List Layout */
|
|
<div className="bg-white rounded-[28px] border border-slate-200/80 shadow-sm overflow-hidden divide-y divide-slate-100">
|
|
{filteredJobs.map((job) => {
|
|
const meta = getJobMetadata(job.title);
|
|
const isFilled = job.hired_count >= job.workers_needed;
|
|
|
|
return (
|
|
<div key={job.id} className="p-6 flex flex-col lg:flex-row lg:items-center justify-between gap-6 hover:bg-slate-50/50 transition-colors">
|
|
<div className="flex items-start space-x-4 flex-1">
|
|
<div className="w-12 h-12 bg-blue-50 rounded-2xl flex items-center justify-center flex-shrink-0 border border-blue-100/50">
|
|
{meta.category === 'Security' ? <Shield className="w-6 h-6 text-[#185FA5]" /> :
|
|
meta.category === 'Driver' ? <Navigation className="w-6 h-6 text-[#185FA5]" /> :
|
|
meta.category === 'Logistics' ? <Layers className="w-6 h-6 text-[#185FA5]" /> :
|
|
<Briefcase className="w-6 h-6 text-[#185FA5]" />}
|
|
</div>
|
|
<div className="space-y-1 flex-1">
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<h3 className="font-extrabold text-slate-900 text-base leading-snug tracking-tight hover:text-[#185FA5] transition-colors">
|
|
<Link href={`/employer/jobs/${job.id}`}>{job.title}</Link>
|
|
</h3>
|
|
<Badge
|
|
variant="outline"
|
|
className={`px-2.5 py-0.5 rounded-full text-[9px] font-black uppercase tracking-wider flex items-center border-0 ${
|
|
job.status.toLowerCase() === 'active' ? 'bg-emerald-50 text-emerald-700' :
|
|
job.status.toLowerCase() === 'closed' ? 'bg-rose-50 text-rose-700' :
|
|
'bg-slate-50 text-slate-500'
|
|
}`}
|
|
>
|
|
{job.status}
|
|
</Badge>
|
|
</div>
|
|
<div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-xs font-bold text-slate-500">
|
|
<span className="uppercase text-[9px] tracking-wider font-extrabold text-slate-400">Posted {job.posted_at}</span>
|
|
<span className="flex items-center space-x-1"><MapPin className="w-3.5 h-3.5 text-slate-400" /> <span>{job.location}</span></span>
|
|
<span className="flex items-center space-x-1"><DollarSign className="w-3.5 h-3.5 text-emerald-600" /> <span>$ {job.salary.toLocaleString()} AED</span></span>
|
|
<span className="text-slate-400">|</span>
|
|
<span className="text-[#185FA5]">{meta.category}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-6 justify-between lg:justify-end">
|
|
<div className="text-right space-y-1">
|
|
<div className="text-xs font-bold text-slate-500">
|
|
Hired: <span className="text-slate-950 font-black">{job.hired_count}</span> / <span className="text-slate-600">{job.workers_needed}</span> ({job.applied_count} applied)
|
|
</div>
|
|
<div className="text-[10px] font-black uppercase tracking-wider">
|
|
{isFilled ? <span className="text-emerald-600">Filled</span> : <span className="text-[#185FA5]">{job.workers_needed - job.hired_count} needed</span>}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-3">
|
|
<Link
|
|
href={`/employer/jobs/${job.id}/applicants`}
|
|
className="bg-[#185FA5] text-white px-5 py-2.5 rounded-xl font-black text-[10px] uppercase tracking-[0.15em] shadow-lg shadow-blue-500/5 hover:bg-[#144f8a] transition-all flex items-center space-x-2"
|
|
>
|
|
<Eye className="w-3.5 h-3.5" />
|
|
<span>Applicants</span>
|
|
</Link>
|
|
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<button className="p-2.5 bg-slate-50 hover:bg-slate-100 border border-slate-100 text-slate-400 hover:text-slate-900 rounded-xl transition-all">
|
|
<MoreHorizontal className="w-4 h-4" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-48 p-2 rounded-2xl shadow-xl border border-slate-100">
|
|
<DropdownMenuLabel className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-2 py-2">Quick Actions</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem className="p-2.5 rounded-xl focus:bg-blue-50 cursor-pointer" asChild>
|
|
<Link href={`/employer/jobs/${job.id}`} className="flex items-center space-x-3 text-slate-700 w-full">
|
|
<Eye className="w-4 h-4 text-blue-500" />
|
|
<span className="text-xs font-bold">View Details</span>
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="p-2.5 rounded-xl focus:bg-blue-50 cursor-pointer" asChild>
|
|
<Link href={`/employer/jobs/${job.id}/edit`} className="flex items-center space-x-3 text-[#185FA5] w-full">
|
|
<Edit2 className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Edit Posting</span>
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem className="p-2.5 rounded-xl focus:bg-rose-50 cursor-pointer" onClick={() => handleDelete(job.id)}>
|
|
<div className="flex items-center space-x-3 text-rose-600 w-full">
|
|
<Trash2 className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Delete Job</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)
|
|
) : (
|
|
/* Empty State */
|
|
<div className="col-span-full py-20 text-center bg-white rounded-[28px] 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>
|
|
</EmployerLayout>
|
|
);
|
|
}
|