311 lines
20 KiB
JavaScript
311 lines
20 KiB
JavaScript
import React from 'react';
|
|
import { Head } from '@inertiajs/react';
|
|
import AdminLayout from '@/Layouts/AdminLayout';
|
|
import {
|
|
Search,
|
|
Filter,
|
|
Download,
|
|
Briefcase,
|
|
Mail,
|
|
MapPin,
|
|
Calendar,
|
|
CheckCircle,
|
|
Clock,
|
|
XCircle,
|
|
ExternalLink,
|
|
MoreVertical,
|
|
BadgeDollarSign
|
|
} from 'lucide-react';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogFooter,
|
|
} from "@/components/ui/dialog";
|
|
|
|
export default function EmployersIndex({ employers: initialEmployers }) {
|
|
const [searchTerm, setSearchTerm] = React.useState('');
|
|
const [statusFilter, setStatusFilter] = React.useState('All');
|
|
const [locationFilter, setLocationFilter] = React.useState('All');
|
|
const [selectedEmployer, setSelectedEmployer] = React.useState(null);
|
|
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
|
|
|
const [employers, setEmployers] = React.useState(initialEmployers || [
|
|
{ id: 1, name: 'Al Barari Real Estate', email: 'hr@albarari.ae', plan: 'Premium Pass', status: 'Active', location: 'Dubai', join_date: '2026-04-10', phone: '+971 50 123 4567', contact_person: 'Ahmed Malik', website: 'www.albarari.ae', company_id: 'TRD-991283' },
|
|
{ id: 2, name: 'Marina Cleaners', email: 'contact@marinaclean.com', plan: 'Basic Search', status: 'Active', location: 'Abu Dhabi', join_date: '2026-04-12', phone: '+971 50 234 5678', contact_person: 'Sarah Johnson', website: 'marinaclean.com', company_id: 'TRD-102938' },
|
|
{ id: 3, name: 'Golden Hospitality', email: 'hiring@golden.hotel', plan: 'None', status: 'Pending', location: 'Sharjah', join_date: '2026-05-01', phone: '+971 50 345 6789', contact_person: 'John Doe', website: 'golden.hotel', company_id: 'TRD-882731' },
|
|
{ id: 4, name: 'Emirates Logistics', email: 'jobs@emirateslogistics.com', plan: 'Premium Pass', status: 'Active', location: 'Dubai', join_date: '2026-03-20', phone: '+971 50 456 7890', contact_person: 'Mike Ross', website: 'emirateslogistics.com', company_id: 'TRD-771625' },
|
|
{ id: 5, name: 'Dubai Mall Services', email: 'support@dubaimall.ae', plan: 'VIP Concierge', status: 'Suspended', location: 'Dubai', join_date: '2026-02-15', phone: '+971 50 567 8901', contact_person: 'Jane Foster', website: 'dubaimall.ae', company_id: 'TRD-660514' },
|
|
]);
|
|
|
|
const filteredEmployers = React.useMemo(() => {
|
|
return employers.filter(emp => {
|
|
const matchesSearch = emp.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
emp.email.toLowerCase().includes(searchTerm.toLowerCase());
|
|
const matchesStatus = statusFilter === 'All' || emp.status === statusFilter;
|
|
const matchesLocation = locationFilter === 'All' || emp.location === locationFilter;
|
|
return matchesSearch && matchesStatus && matchesLocation;
|
|
});
|
|
}, [employers, searchTerm, statusFilter, locationFilter]);
|
|
|
|
const handleApprove = (id) => {
|
|
setEmployers(employers.map(emp =>
|
|
emp.id === id ? { ...emp, status: 'Active' } : emp
|
|
));
|
|
};
|
|
|
|
const handleAction = (emp) => {
|
|
setSelectedEmployer(emp);
|
|
setIsDialogOpen(true);
|
|
};
|
|
|
|
return (
|
|
<AdminLayout title="Employer Management">
|
|
<Head title="Employers" />
|
|
|
|
<div className="space-y-6">
|
|
{/* Search and Filters */}
|
|
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
|
<div className="flex flex-1 items-center space-x-3">
|
|
<div className="relative flex-1 max-w-md">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
|
|
<input
|
|
type="text"
|
|
placeholder="Search by name or email..."
|
|
className="w-full pl-10 pr-4 py-2 bg-white border border-slate-200 rounded-xl text-sm font-medium focus:ring-4 focus:ring-teal-500/10 outline-none transition-all"
|
|
value={searchTerm}
|
|
onChange={e => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center space-x-2">
|
|
<select
|
|
className="px-4 py-2 bg-white border border-slate-200 rounded-xl text-xs font-bold text-slate-600 outline-none focus:ring-2 focus:ring-teal-500/10 transition-all"
|
|
value={statusFilter}
|
|
onChange={e => setStatusFilter(e.target.value)}
|
|
>
|
|
<option value="All">All Status</option>
|
|
<option value="Active">Active</option>
|
|
<option value="Pending">Pending</option>
|
|
<option value="Suspended">Suspended</option>
|
|
</select>
|
|
|
|
<select
|
|
className="px-4 py-2 bg-white border border-slate-200 rounded-xl text-xs font-bold text-slate-600 outline-none focus:ring-2 focus:ring-teal-500/10 transition-all"
|
|
value={locationFilter}
|
|
onChange={e => setLocationFilter(e.target.value)}
|
|
>
|
|
<option value="All">All Locations</option>
|
|
<option value="Dubai">Dubai</option>
|
|
<option value="Abu Dhabi">Abu Dhabi</option>
|
|
<option value="Sharjah">Sharjah</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-3">
|
|
<button className="flex items-center space-x-2 px-6 py-2 bg-[#0F6E56] text-white rounded-xl text-sm font-bold shadow-lg shadow-teal-500/10 hover:bg-[#085041] transition-all">
|
|
<Download className="w-4 h-4" />
|
|
<span>Export CSV</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Employers Table */}
|
|
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-slate-50/50 hover:bg-slate-50/50">
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14 pl-8">Employer Info</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Location</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Plan</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Reg. Date</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14">Status</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest h-14 text-right pr-8">Action</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{filteredEmployers.map((emp) => (
|
|
<TableRow key={emp.id} className="hover:bg-slate-50/50 transition-colors">
|
|
<TableCell className="py-5 pl-8">
|
|
<div className="flex items-center space-x-4">
|
|
<div className="w-10 h-10 rounded-xl bg-slate-50 flex items-center justify-center border border-slate-100">
|
|
<Briefcase className="w-5 h-5 text-slate-400" />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-sm font-bold text-slate-900 leading-none mb-1">{emp.name}</h4>
|
|
<p className="text-xs text-slate-500 font-medium">{emp.email}</p>
|
|
</div>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center space-x-2 text-slate-600">
|
|
<MapPin className="w-3.5 h-3.5 text-slate-400" />
|
|
<span className="text-xs font-bold">{emp.location}</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell>
|
|
<span className={`px-2.5 py-1 rounded-lg text-[10px] font-black uppercase tracking-tight ${
|
|
emp.plan === 'None' ? 'bg-slate-100 text-slate-500' : 'bg-blue-50 text-blue-600'
|
|
}`}>
|
|
{emp.plan}
|
|
</span>
|
|
</TableCell>
|
|
<TableCell className="text-xs font-bold text-slate-400">
|
|
{emp.join_date}
|
|
</TableCell>
|
|
<TableCell>
|
|
<div className="flex items-center space-x-2">
|
|
{emp.status === 'Active' && <CheckCircle className="w-4 h-4 text-emerald-500" />}
|
|
{emp.status === 'Pending' && <Clock className="w-4 h-4 text-amber-500" />}
|
|
{emp.status === 'Suspended' && <XCircle className="w-4 h-4 text-rose-500" />}
|
|
<span className={`text-[10px] font-black uppercase tracking-widest ${
|
|
emp.status === 'Active' ? 'text-emerald-600' :
|
|
emp.status === 'Pending' ? 'text-amber-600' : 'text-rose-600'
|
|
}`}>
|
|
{emp.status}
|
|
</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="text-right pr-8">
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<button className="p-2 text-slate-400 hover:text-slate-900 hover:bg-slate-50 rounded-lg transition-all outline-none">
|
|
<MoreVertical className="w-5 h-5" />
|
|
</button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-48 p-2 rounded-xl">
|
|
<DropdownMenuLabel className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-2 py-2">Actions</DropdownMenuLabel>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem className="p-3 rounded-lg focus:bg-slate-50 cursor-pointer" onClick={() => handleAction(emp)}>
|
|
<div className="flex items-center space-x-3">
|
|
<ExternalLink className="w-4 h-4 text-slate-500" />
|
|
<span className="text-xs font-bold">View Details</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
{emp.status === 'Pending' && (
|
|
<DropdownMenuItem className="p-3 rounded-lg focus:bg-emerald-50 cursor-pointer text-emerald-600" onClick={() => handleApprove(emp.id)}>
|
|
<div className="flex items-center space-x-3">
|
|
<CheckCircle className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Approve Employer</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
)}
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem className="p-3 rounded-lg focus:bg-rose-50 cursor-pointer text-rose-600">
|
|
<div className="flex items-center space-x-3">
|
|
<XCircle className="w-4 h-4" />
|
|
<span className="text-xs font-bold">Suspend Account</span>
|
|
</div>
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
|
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
<DialogContent className="max-w-2xl bg-white p-0 overflow-hidden rounded-[32px] border-none shadow-2xl">
|
|
<div className="bg-[#0F6E56] p-8 text-white relative">
|
|
<div className="absolute top-0 right-0 p-8 opacity-10">
|
|
<Briefcase className="w-32 h-32" />
|
|
</div>
|
|
<div className="relative z-10 flex items-center space-x-4">
|
|
<div className="w-16 h-16 rounded-2xl bg-white/20 backdrop-blur-md flex items-center justify-center border border-white/20">
|
|
<Briefcase className="w-8 h-8 text-white" />
|
|
</div>
|
|
<div>
|
|
<h2 className="text-2xl font-black tracking-tight">{selectedEmployer?.name}</h2>
|
|
<p className="text-teal-100 text-sm font-medium">{selectedEmployer?.location} • Registered {selectedEmployer?.join_date}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="p-8 space-y-8">
|
|
<div className="grid grid-cols-2 gap-8">
|
|
<div className="space-y-4">
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Primary Email</label>
|
|
<div className="flex items-center space-x-2 text-sm font-bold text-slate-900">
|
|
<Mail className="w-4 h-4 text-teal-600" />
|
|
<span>{selectedEmployer?.email}</span>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Phone Number</label>
|
|
<div className="flex items-center space-x-2 text-sm font-bold text-slate-900">
|
|
<Clock className="w-4 h-4 text-teal-600" />
|
|
<span>{selectedEmployer?.phone}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-4">
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Contact Person</label>
|
|
<div className="text-sm font-bold text-slate-900">{selectedEmployer?.contact_person}</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Company ID</label>
|
|
<div className="text-sm font-bold text-slate-900">{selectedEmployer?.company_id}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-slate-50 rounded-2xl p-6 border border-slate-100 flex items-center justify-between">
|
|
<div className="space-y-1">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Subscription Plan</label>
|
|
<div className="flex items-center space-x-2">
|
|
<BadgeDollarSign className="w-4 h-4 text-blue-600" />
|
|
<span className="text-sm font-black text-blue-600">{selectedEmployer?.plan}</span>
|
|
</div>
|
|
</div>
|
|
<div className="text-right">
|
|
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Current Status</label>
|
|
<div className={`text-xs font-black uppercase tracking-widest ${
|
|
selectedEmployer?.status === 'Active' ? 'text-emerald-600' : 'text-amber-600'
|
|
}`}>
|
|
{selectedEmployer?.status}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-end space-x-3 pt-4 border-t border-slate-100">
|
|
{selectedEmployer?.status === 'Pending' && (
|
|
<button
|
|
onClick={() => { handleApprove(selectedEmployer.id); setIsDialogOpen(false); }}
|
|
className="px-6 py-2.5 bg-[#0F6E56] text-white rounded-xl text-[10px] font-black uppercase tracking-widest shadow-lg shadow-teal-500/10 hover:bg-[#085041] transition-all"
|
|
>
|
|
Approve Employer
|
|
</button>
|
|
)}
|
|
<button className="px-6 py-2.5 bg-slate-900 text-white rounded-xl text-[10px] font-black uppercase tracking-widest hover:bg-slate-800 transition-all">
|
|
Send Message
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</AdminLayout>
|
|
);
|
|
}
|