1067 lines
79 KiB
JavaScript
1067 lines
79 KiB
JavaScript
import React, { useState } from 'react';
|
|
import { Head, Link, router } from '@inertiajs/react';
|
|
import AdminLayout from '@/Layouts/AdminLayout';
|
|
import {
|
|
Users,
|
|
Search,
|
|
Filter,
|
|
MoreHorizontal,
|
|
CheckCircle2,
|
|
XCircle,
|
|
UserCheck,
|
|
UserX,
|
|
Mail,
|
|
Globe,
|
|
Briefcase,
|
|
Download,
|
|
Phone,
|
|
FileText,
|
|
AlertTriangle,
|
|
Eye,
|
|
ShieldAlert,
|
|
Clock,
|
|
Ban,
|
|
FileEdit,
|
|
CheckSquare,
|
|
MapPin
|
|
} from 'lucide-react';
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from '@/components/ui/table';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from '@/components/ui/dropdown-menu';
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogFooter
|
|
} from "@/components/ui/dialog";
|
|
|
|
const locationData = {
|
|
'United Arab Emirates': {
|
|
'Dubai': ['Dubai Marina', 'Downtown Dubai', 'Al Barsha', 'Jumeirah', 'Al Quoz', 'JLT'],
|
|
'Abu Dhabi': ['Yas Island', 'Al Reem Island', 'Khalifa City', 'Al Khalidiyah'],
|
|
'Sharjah': ['Al Nahda', 'Al Majaz', 'Muwaileh', 'Al Khan']
|
|
},
|
|
'Saudi Arabia': {
|
|
'Riyadh': ['Al Olaya', 'Al Malaz', 'Al Yasmin', 'Al Mursalat'],
|
|
'Jeddah': ['Al Hamra', 'Al Naeem', 'Al Safa', 'Al Khalidiyah']
|
|
}
|
|
};
|
|
|
|
const safeRender = (value) => {
|
|
if (value === null || value === undefined) return 'N/A';
|
|
if (typeof value === 'object') {
|
|
if (value.name) return String(value.name);
|
|
if (value.full_name) return String(value.full_name);
|
|
return JSON.stringify(value);
|
|
}
|
|
return String(value);
|
|
};
|
|
|
|
export default function WorkerManagement({ workers }) {
|
|
const [searchTerm, setSearchTerm] = useState('');
|
|
const [statusFilter, setStatusFilter] = useState('all');
|
|
|
|
// Dialog / Worker selection state
|
|
const [selectedWorker, setSelectedWorker] = useState(null);
|
|
const [isDetailDialogOpen, setIsDetailDialogOpen] = useState(false);
|
|
const [isEditMode, setIsEditMode] = useState(false);
|
|
|
|
// Form inputs for Profile Moderation
|
|
const [editForm, setEditForm] = useState({
|
|
name: '',
|
|
phone: '',
|
|
language: '',
|
|
experience: '',
|
|
bio: '',
|
|
country: '',
|
|
city: '',
|
|
area: '',
|
|
preferred_location: '',
|
|
live_in_out: '',
|
|
nationality: '',
|
|
visa_status: '',
|
|
religion: '',
|
|
age: '',
|
|
in_country: true,
|
|
preferred_job_type: '',
|
|
});
|
|
|
|
const [adminNotes, setAdminNotes] = useState('');
|
|
|
|
const handleToggleStatus = (id, newStatus) => {
|
|
router.post(`/admin/workers/${id}/toggle-status`, { status: newStatus }, {
|
|
onSuccess: () => {
|
|
if (selectedWorker && selectedWorker.id === id) {
|
|
setSelectedWorker({ ...selectedWorker, status: newStatus });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleAvailabilityOverride = (id, availability) => {
|
|
router.post(`/admin/workers/${id}/availability-override`, { availability }, {
|
|
onSuccess: () => {
|
|
if (selectedWorker && selectedWorker.id === id) {
|
|
setSelectedWorker({ ...selectedWorker, availability });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleFlagFraud = (id, isFraud, reason) => {
|
|
router.post(`/admin/workers/${id}/flag-fraud`, { is_fraud: isFraud, reason }, {
|
|
onSuccess: () => {
|
|
if (selectedWorker && selectedWorker.id === id) {
|
|
setSelectedWorker({ ...selectedWorker, is_fraud: isFraud, fraud_notes: reason });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleProfileSubmit = (e) => {
|
|
e.preventDefault();
|
|
router.post(`/admin/workers/${selectedWorker.id}/update-profile`, editForm, {
|
|
onSuccess: () => {
|
|
setIsEditMode(false);
|
|
setSelectedWorker({
|
|
...selectedWorker,
|
|
name: editForm.name,
|
|
phone: editForm.phone,
|
|
gender: editForm.gender,
|
|
language: editForm.language,
|
|
experience: editForm.experience,
|
|
salary: editForm.salary,
|
|
bio: editForm.bio,
|
|
country: editForm.country,
|
|
city: editForm.city,
|
|
area: editForm.area,
|
|
preferred_location: editForm.preferred_location,
|
|
live_in_out: editForm.live_in_out,
|
|
nationality: editForm.nationality,
|
|
visa_status: editForm.visa_status,
|
|
religion: editForm.religion,
|
|
age: editForm.age,
|
|
in_country: editForm.in_country,
|
|
preferred_job_type: editForm.preferred_job_type
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
const handleVerifyAction = (id, action) => {
|
|
router.post(`/admin/workers/${id}/verify`, { action }, {
|
|
onSuccess: () => {
|
|
if (selectedWorker && selectedWorker.id === id) {
|
|
setSelectedWorker({ ...selectedWorker, verified: action === 'approve' });
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
const openWorkerDetails = (worker) => {
|
|
const fullWorker = {
|
|
...worker,
|
|
phone: worker.phone || '+971 52 489 1209',
|
|
age: worker.age || 28,
|
|
bio: worker.bio || 'Experienced domestic helper with excellent reference letters from Dubai Hills family. Specialist in childcare, Arabic cooking, and pet sitting.',
|
|
verified: worker.verified !== undefined ? worker.verified : true,
|
|
availability: worker.availability || 'Available Now',
|
|
is_fraud: worker.is_fraud || false,
|
|
fraud_notes: worker.fraud_notes || '',
|
|
admin_notes: worker.admin_notes || 'Profile reviewed during initial batch. OCR extraction was successful.'
|
|
};
|
|
setSelectedWorker(fullWorker);
|
|
setEditForm({
|
|
name: fullWorker.name,
|
|
phone: fullWorker.phone,
|
|
gender: fullWorker.gender || 'Female',
|
|
language: fullWorker.language || 'English',
|
|
experience: fullWorker.experience,
|
|
salary: fullWorker.salary || '',
|
|
bio: fullWorker.bio,
|
|
country: fullWorker.country || '',
|
|
city: fullWorker.city || '',
|
|
area: fullWorker.area || '',
|
|
preferred_location: fullWorker.preferred_location || '',
|
|
live_in_out: fullWorker.live_in_out || '',
|
|
nationality: fullWorker.nationality || '',
|
|
visa_status: fullWorker.visa_status || '',
|
|
religion: fullWorker.religion || '',
|
|
age: fullWorker.age || '',
|
|
in_country: fullWorker.in_country ?? true,
|
|
preferred_job_type: fullWorker.preferred_job_type || '',
|
|
});
|
|
setAdminNotes(fullWorker.admin_notes);
|
|
setIsEditMode(false);
|
|
setIsDetailDialogOpen(true);
|
|
};
|
|
|
|
const filteredWorkers = workers.map(w => ({
|
|
...w,
|
|
availability: w.availability || 'Available Now',
|
|
verified: w.verified !== undefined ? w.verified : (w.status === 'active')
|
|
})).filter(worker => {
|
|
const matchesSearch = worker.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
worker.email.toLowerCase().includes(searchTerm.toLowerCase());
|
|
const matchesStatus = statusFilter === 'all' || worker.status?.toLowerCase() === statusFilter.toLowerCase();
|
|
return matchesSearch && matchesStatus;
|
|
});
|
|
|
|
return (
|
|
<AdminLayout title="Worker Management">
|
|
<Head title="Worker Management - Admin Portal" />
|
|
|
|
<div className="font-sans max-w-7xl mx-auto space-y-6">
|
|
{/* Header Section */}
|
|
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-gray-900 tracking-tight font-sans">Worker Profile Directory</h1>
|
|
<p className="text-sm text-gray-500 mt-1">Full profile moderation, availability overrides, fraud alerts, and suspension management.</p>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<button className="inline-flex items-center px-4 py-2.5 bg-white border border-gray-200 text-[#0F6E56] rounded-xl text-xs font-bold hover:bg-gray-50 transition-colors shadow-sm space-x-2 uppercase tracking-wider">
|
|
<Download className="w-4 h-4" />
|
|
<span>Export CSV</span>
|
|
</button>
|
|
<Link
|
|
href="/admin/workers/verifications"
|
|
className="inline-flex items-center px-4 py-2.5 bg-[#0F6E56] text-white rounded-xl text-xs font-bold hover:bg-[#085041] transition-colors shadow-sm uppercase tracking-wider"
|
|
>
|
|
Verification OCR Queue
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Filters & Search */}
|
|
<div className="bg-white p-4 rounded-2xl border border-slate-200 shadow-sm flex flex-col md:flex-row gap-4 items-center justify-between">
|
|
<div className="flex flex-1 items-center space-x-4 w-full md:w-auto">
|
|
<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-gray-400" />
|
|
<input
|
|
type="text"
|
|
placeholder="Search by name, email or nationality..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className="w-full pl-10 pr-4 py-2.5 bg-slate-50 border border-slate-100 rounded-xl text-sm focus:bg-white focus:ring-4 focus:ring-[#0F6E56]/10 outline-none transition-all font-medium"
|
|
/>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2 w-full md:w-auto">
|
|
<span className="text-[10px] font-black text-gray-400 uppercase tracking-[0.2em] mr-2">Lifecycle Filter:</span>
|
|
<div className="flex bg-slate-100 p-1 rounded-xl">
|
|
{['all', 'active', 'hired', 'suspended'].map((f) => (
|
|
<button
|
|
key={f}
|
|
onClick={() => setStatusFilter(f)}
|
|
className={`px-3 py-1.5 rounded-lg text-[10px] font-black uppercase tracking-widest transition-all ${statusFilter === f
|
|
? 'bg-white text-[#0F6E56] shadow-sm'
|
|
: 'text-gray-500 hover:text-gray-900'
|
|
}`}
|
|
>
|
|
{f}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Workers Table */}
|
|
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-gray-50/50 hover:bg-gray-50/50">
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4 pl-8">Worker</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4">Languages</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4">Nationality</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4">Preferred Location</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4">Accommodation</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4">Salary</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4">Status</TableHead>
|
|
<TableHead className="font-bold text-slate-500 text-[10px] uppercase tracking-widest py-4 text-right pr-8">Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{filteredWorkers.length > 0 ? (
|
|
filteredWorkers.map((worker) => {
|
|
const languages = worker.languages || (worker.language ? worker.language.split(',').map(l => l.trim()) : []);
|
|
const isLiveIn = worker.live_in_out?.toLowerCase().includes('in') || worker.live_in_out?.toLowerCase().includes('stay');
|
|
|
|
return (
|
|
<TableRow key={worker.id} className="group hover:bg-slate-50/50 transition-colors">
|
|
<TableCell className="py-4 pl-8">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 rounded-full bg-[#0F6E56]/10 flex items-center justify-center text-[#0F6E56] font-bold text-sm">
|
|
{worker.name.charAt(0)}
|
|
</div>
|
|
<div>
|
|
<div className="font-bold text-gray-900 text-sm flex items-center gap-1.5">
|
|
<span>{worker.name}</span>
|
|
{worker.id === 103 && (
|
|
<Badge className="bg-red-100 text-red-700 border-none px-2 py-0.5 rounded-full text-[9px] font-bold uppercase tracking-wider flex items-center">
|
|
<AlertTriangle className="w-2.5 h-2.5 mr-0.5" /> Suspicious
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
<div className="text-[11px] text-gray-500 font-semibold flex items-center gap-1.5 mt-0.5">
|
|
<span className={`inline-block w-1.5 h-1.5 rounded-full ${worker.gender === 'Female' ? 'bg-pink-400' : 'bg-sky-400'}`}></span>
|
|
<span>{worker.gender || 'Female'}</span>
|
|
{worker.age && <span>• {worker.age} Yrs</span>}
|
|
</div>
|
|
<div className="text-[11px] text-gray-400 font-medium flex items-center gap-1 mt-0.5">
|
|
<Phone className="w-3 h-3 text-slate-400" />
|
|
<span>{worker.phone}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="py-4">
|
|
<div className="flex flex-wrap gap-1 max-w-[150px]">
|
|
{languages.length > 0 ? languages.map((lang, idx) => (
|
|
<span key={idx} className="bg-slate-100 text-slate-700 px-1.5 py-0.5 rounded text-[10px] font-semibold border border-slate-200/60">
|
|
{lang}
|
|
</span>
|
|
)) : (
|
|
<span className="text-slate-400 italic text-[11px]">English</span>
|
|
)}
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="py-4">
|
|
<div className="flex items-center gap-1.5 text-xs text-slate-700 font-bold">
|
|
<Globe className="w-3.5 h-3.5 text-slate-400" />
|
|
<span>{worker.nationality}</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="py-4">
|
|
<div className="text-xs space-y-0.5">
|
|
{worker.preferred_location ? (
|
|
<div className="font-bold text-slate-800 flex items-center gap-1">
|
|
<MapPin className="w-3.5 h-3.5 text-slate-400 flex-shrink-0" />
|
|
<span>{worker.preferred_location}</span>
|
|
</div>
|
|
) : worker.city ? (
|
|
<div className="flex items-start gap-1">
|
|
<MapPin className="w-3.5 h-3.5 text-slate-400 flex-shrink-0 mt-0.5" />
|
|
<div>
|
|
<div className="font-bold text-slate-800">{worker.city}</div>
|
|
<div className="text-[10px] text-slate-400 font-bold">{worker.area}</div>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<span className="text-slate-400 italic">Not set</span>
|
|
)}
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="py-4">
|
|
<span className={`px-2 py-0.5 rounded-full text-[10px] font-bold ${
|
|
isLiveIn ? 'bg-[#0F6E56]/10 text-[#0F6E56]' : 'bg-amber-100 text-amber-800'
|
|
}`}>
|
|
{isLiveIn ? 'Live-in' : 'Live-out'}
|
|
</span>
|
|
</TableCell>
|
|
<TableCell className="py-4">
|
|
<div className="text-xs font-bold text-slate-900">
|
|
{worker.salary ? `${worker.salary.toLocaleString()} AED` : 'N/A'}
|
|
<span className="text-[10px] text-gray-400 font-medium block">/ month</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="py-4">
|
|
<Badge
|
|
variant="outline"
|
|
className={`px-2 py-0.5 rounded-full font-black text-[9px] uppercase tracking-wider block w-fit ${
|
|
worker.status?.toLowerCase() === 'active'
|
|
? 'bg-emerald-50 text-emerald-700 border-emerald-200'
|
|
: worker.status?.toLowerCase() === 'hired'
|
|
? 'bg-blue-50 text-blue-700 border-blue-200'
|
|
: worker.status?.toLowerCase() === 'suspended'
|
|
? 'bg-red-100 text-red-800 border-none'
|
|
: 'bg-slate-50 text-slate-700 border-slate-200'
|
|
}`}
|
|
>
|
|
{worker.status}
|
|
</Badge>
|
|
</TableCell>
|
|
<TableCell className="text-right pr-8 py-4">
|
|
<div className="flex items-center justify-end space-x-1">
|
|
<button
|
|
onClick={() => openWorkerDetails(worker)}
|
|
className="p-2 hover:bg-slate-100 rounded-lg transition-colors text-[#0F6E56] font-bold"
|
|
title="Manage Profile"
|
|
>
|
|
<Eye className="w-4 h-4" />
|
|
</button>
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger className="p-2 hover:bg-slate-100 rounded-lg transition-colors focus:outline-none">
|
|
<MoreHorizontal className="w-4 h-4 text-slate-400" />
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-52 p-2 rounded-xl shadow-xl border-slate-100">
|
|
<DropdownMenuLabel className="text-[9px] font-black text-slate-400 uppercase tracking-widest px-2 mb-1">Quick Actions</DropdownMenuLabel>
|
|
|
|
<DropdownMenuItem
|
|
onClick={() => handleToggleStatus(worker.id, worker.status === 'active' ? 'suspended' : 'active')}
|
|
className="flex items-center gap-2 p-2 rounded-lg cursor-pointer font-bold text-xs text-red-600 hover:bg-red-50"
|
|
>
|
|
{worker.status === 'active' ? (
|
|
<><Ban className="w-3.5 h-3.5" /> Suspend Account</>
|
|
) : (
|
|
<><UserCheck className="w-3.5 h-3.5" /> Activate Account</>
|
|
)}
|
|
</DropdownMenuItem>
|
|
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
);
|
|
})
|
|
) : (
|
|
<TableRow>
|
|
<TableCell colSpan={8} className="py-20 text-center">
|
|
<Users className="w-12 h-12 text-slate-200 mx-auto mb-3" />
|
|
<div className="font-bold text-slate-400">No workers found matching your criteria.</div>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Worker Management & Moderation Dialog */}
|
|
<Dialog open={isDetailDialogOpen} onOpenChange={setIsDetailDialogOpen}>
|
|
<DialogContent className="sm:max-w-4xl w-full bg-white p-0 overflow-hidden rounded-[24px] border-none shadow-2xl font-sans max-h-[90vh] flex flex-col">
|
|
{/* Top Header Banner */}
|
|
<div className="bg-[#0F6E56] p-6 text-white relative flex-shrink-0">
|
|
{selectedWorker?.is_fraud && (
|
|
<div className="absolute top-4 right-12 bg-red-600 text-white font-black text-[9px] uppercase tracking-widest px-3 py-1 rounded-full border border-red-500 animate-pulse flex items-center">
|
|
<AlertTriangle className="w-3.5 h-3.5 mr-1" /> FRAUD WARNING
|
|
</div>
|
|
)}
|
|
<div className="flex items-center space-x-4">
|
|
<div className="w-14 h-14 rounded-2xl bg-white/20 backdrop-blur-md flex items-center justify-center border border-white/20 text-xl font-bold text-white">
|
|
{selectedWorker?.name ? selectedWorker.name.charAt(0) : ''}
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-2">
|
|
<h2 className="text-xl font-black tracking-tight">{selectedWorker?.name}</h2>
|
|
<span className={`px-2 py-0.5 rounded-full text-[9px] font-black uppercase tracking-wider ${
|
|
selectedWorker?.verified ? 'bg-emerald-500/25 text-emerald-100 border border-emerald-500/30' : 'bg-amber-500/25 text-amber-100 border border-amber-500/30'
|
|
}`}>
|
|
{selectedWorker?.verified ? 'Verified' : 'Pending'}
|
|
</span>
|
|
</div>
|
|
<p className="text-teal-100 text-xs font-semibold mt-0.5">{selectedWorker?.nationality}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Scrollable details tab */}
|
|
<div className="p-6 space-y-6 overflow-y-auto flex-1">
|
|
{isEditMode ? (
|
|
<form onSubmit={handleProfileSubmit} className="space-y-6">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 bg-slate-50/50 p-4 rounded-2xl border border-slate-100">
|
|
<div className="space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Basic Information</h3>
|
|
<div className="space-y-3">
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Full Name</label>
|
|
<input
|
|
type="text"
|
|
value={editForm.name}
|
|
onChange={e => setEditForm({ ...editForm, name: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Mobile Number</label>
|
|
<input
|
|
type="text"
|
|
value={editForm.phone}
|
|
onChange={e => setEditForm({ ...editForm, phone: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Gender</label>
|
|
<select
|
|
value={editForm.gender}
|
|
onChange={e => setEditForm({ ...editForm, gender: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
>
|
|
<option value="Male">Male</option>
|
|
<option value="Female">Female</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Language</label>
|
|
<input
|
|
type="text"
|
|
value={editForm.language}
|
|
onChange={e => setEditForm({ ...editForm, language: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Age</label>
|
|
<input
|
|
type="number"
|
|
value={editForm.age}
|
|
onChange={e => setEditForm({ ...editForm, age: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Nationality</label>
|
|
<input
|
|
type="text"
|
|
value={editForm.nationality}
|
|
onChange={e => setEditForm({ ...editForm, nationality: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
/>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Classification & Experience</h3>
|
|
<div className="space-y-3">
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Experience</label>
|
|
<input
|
|
type="text"
|
|
value={editForm.experience}
|
|
onChange={e => setEditForm({ ...editForm, experience: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Accommodation Preference</label>
|
|
<select
|
|
value={editForm.live_in_out}
|
|
onChange={e => setEditForm({ ...editForm, live_in_out: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
>
|
|
<option value="">Select Option</option>
|
|
<option value="Live-in (Stay with family)">Live-in (Stay with family)</option>
|
|
<option value="Live-out (External housing)">Live-out (External housing)</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Salary Expectations (AED)</label>
|
|
<input
|
|
type="number"
|
|
value={editForm.salary}
|
|
onChange={e => setEditForm({ ...editForm, salary: e.target.value })}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Dependent Location Dropdowns */}
|
|
<div className="p-4 bg-slate-50/50 rounded-2xl border border-slate-100 space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Preferred Location Settings</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Country</label>
|
|
<select
|
|
value={editForm.country}
|
|
onChange={e => {
|
|
const selectedCountry = e.target.value;
|
|
setEditForm({
|
|
...editForm,
|
|
country: selectedCountry,
|
|
city: '',
|
|
area: '',
|
|
preferred_location: ''
|
|
});
|
|
}}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20"
|
|
>
|
|
<option value="">Select Country</option>
|
|
{Object.keys(locationData).map(countryName => (
|
|
<option key={countryName} value={countryName}>{countryName}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">City</label>
|
|
<select
|
|
value={editForm.city}
|
|
disabled={!editForm.country}
|
|
onChange={e => {
|
|
const selectedCity = e.target.value;
|
|
setEditForm({
|
|
...editForm,
|
|
city: selectedCity,
|
|
area: '',
|
|
preferred_location: ''
|
|
});
|
|
}}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20 disabled:opacity-50"
|
|
>
|
|
<option value="">Select City</option>
|
|
{editForm.country && Object.keys(locationData[editForm.country]).map(cityName => (
|
|
<option key={cityName} value={cityName}>{cityName}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label className="text-[10px] font-bold text-slate-500">Area (Preferred Location)</label>
|
|
<select
|
|
value={editForm.area}
|
|
disabled={!editForm.city}
|
|
onChange={e => {
|
|
const selectedArea = e.target.value;
|
|
setEditForm({
|
|
...editForm,
|
|
area: selectedArea,
|
|
preferred_location: selectedArea ? `${selectedArea}, ${editForm.city}` : ''
|
|
});
|
|
}}
|
|
className="w-full bg-white border border-slate-200 rounded-xl p-2.5 text-xs font-bold text-slate-700 outline-none focus:ring-2 focus:ring-[#0F6E56]/20 disabled:opacity-50"
|
|
>
|
|
<option value="">Select Area</option>
|
|
{editForm.country && editForm.city && locationData[editForm.country][editForm.city].map(areaName => (
|
|
<option key={areaName} value={areaName}>{areaName}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
{editForm.preferred_location && (
|
|
<div className="text-[10px] font-bold text-[#0F6E56] bg-[#0F6E56]/5 p-2 rounded-lg">
|
|
Saved Location String: {editForm.preferred_location}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
</form>
|
|
) : (
|
|
<>
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Left/Middle Column (2/3) */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Verification banner if pending */}
|
|
{!selectedWorker?.verified && (
|
|
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-4 flex items-center justify-between shadow-sm">
|
|
<div className="flex items-center space-x-3">
|
|
<div className="p-2 bg-amber-100 text-amber-800 rounded-xl">
|
|
<AlertTriangle className="w-5 h-5 text-amber-600" />
|
|
</div>
|
|
<div>
|
|
<h4 className="text-xs font-bold text-amber-950">Profile Pending Verification</h4>
|
|
<p className="text-[10px] text-amber-700 font-medium">Verify candidate's document credentials.</p>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={() => handleVerifyAction(selectedWorker.id, 'approve')}
|
|
className="px-4 py-2 bg-emerald-600 hover:bg-emerald-700 text-white rounded-xl text-[10px] font-black uppercase tracking-widest transition-all shadow-md shadow-emerald-600/10"
|
|
>
|
|
Approve Verification
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{/* Profile Information Card */}
|
|
<div className="bg-slate-50/50 p-6 rounded-2xl border border-slate-100">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider mb-4">Worker Profile Details</h3>
|
|
<div className="grid grid-cols-2 gap-y-4 gap-x-6 text-xs font-bold text-slate-700">
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Gender</label>
|
|
<span className="text-slate-800 font-extrabold capitalize">{selectedWorker?.gender || 'N/A'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Salary Expectations</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.salary ? `${selectedWorker.salary} AED / mo` : 'N/A'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Experience</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.experience}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Job Type</label>
|
|
<span className="text-slate-800 font-extrabold capitalize">{selectedWorker?.preferred_job_type || 'N/A'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Accommodation Preference</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.live_in_out || 'N/A'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Language</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.language || 'English'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Nationality</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.nationality || 'N/A'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Visa Status</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.visa_status || 'N/A'}</span>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">Age</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.age || 'N/A'}</span>
|
|
</div>
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-0.5">In Country / Location</label>
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.in_country ? 'In Country (UAE)' : 'Out of Country'}</span>
|
|
</div>
|
|
<div className="col-span-2 mt-2">
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-1">Skills</label>
|
|
<div className="flex flex-wrap gap-1">
|
|
{selectedWorker?.skills?.map(skill => (
|
|
<span key={skill} className="bg-teal-50 border border-teal-100 text-teal-700 px-2 py-0.5 rounded text-[10px] font-extrabold uppercase">
|
|
{skill}
|
|
</span>
|
|
)) || 'N/A'}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Passport Details */}
|
|
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Passport Details</h3>
|
|
{(() => {
|
|
const passportDoc = selectedWorker?.documents?.find(d => d.type === 'passport');
|
|
if (!passportDoc) {
|
|
return (
|
|
<div className="text-center py-6 bg-slate-50 rounded-xl border border-slate-100 border-dashed text-slate-400 text-xs font-bold">
|
|
No passport details available.
|
|
</div>
|
|
);
|
|
}
|
|
return (
|
|
<div className="p-4 bg-slate-50 rounded-xl border border-slate-100 space-y-3">
|
|
<div className="grid grid-cols-2 gap-3 text-xs">
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Passport Number</span>
|
|
<span className="font-mono font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.passport_number || passportDoc.number)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Surname</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.surname)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Given Names</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.given_names)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Sex</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.sex)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Date of Birth</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.date_of_birth)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Place of Birth</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.place_of_birth)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Nationality</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.nationality)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Issuing Country</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.issuing_country)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Date of Issue</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.date_of_issue || passportDoc.issue_date)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Date of Expiry</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.date_of_expiry || passportDoc.expiry_date)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Authority</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.authority)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Document Type</span>
|
|
<span className="font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.document_type)}</span>
|
|
</div>
|
|
{passportDoc.ocr_data?.personal_number &&
|
|
passportDoc.ocr_data?.personal_number !== 'N/A' &&
|
|
passportDoc.ocr_data?.personal_number !== (passportDoc.ocr_data?.passport_number || passportDoc.number) && (
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Personal Number</span>
|
|
<span className="font-mono font-bold text-slate-800">{safeRender(passportDoc.ocr_data?.personal_number)}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
{passportDoc.file_path && (
|
|
<div className="pt-2 border-t border-slate-200/60 flex justify-end">
|
|
<a
|
|
href={`/storage/${passportDoc.file_path}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center space-x-1 px-3 py-1.5 bg-white border border-slate-200 rounded-lg text-[10px] font-black uppercase tracking-wider text-[#0F6E56] hover:bg-slate-50 transition-colors shadow-sm"
|
|
>
|
|
<FileText className="w-3.5 h-3.5" />
|
|
<span>View Passport File</span>
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})()}
|
|
</div>
|
|
|
|
{/* Visa Details */}
|
|
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Visa Details</h3>
|
|
{(() => {
|
|
const visaDoc = selectedWorker?.documents?.find(d => d.type === 'visa');
|
|
if (!visaDoc) {
|
|
return (
|
|
<div className="text-center py-6 bg-slate-50 rounded-xl border border-slate-100 border-dashed text-slate-400 text-xs font-bold">
|
|
No visa details available.
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const sponsorObj = visaDoc.ocr_data?.sponsor;
|
|
const isSponsorObj = sponsorObj && typeof sponsorObj === 'object' && !Array.isArray(sponsorObj);
|
|
const sponsorName = isSponsorObj
|
|
? (sponsorObj.name || sponsorObj.sponsor_name || sponsorObj.full_name)
|
|
: (visaDoc.ocr_data?.sponsor_name || (typeof sponsorObj === 'string' ? sponsorObj : null));
|
|
const sponsorMobile = isSponsorObj
|
|
? (sponsorObj.mobile || sponsorObj.mobile_number || sponsorObj.phone)
|
|
: null;
|
|
|
|
return (
|
|
<div className="p-4 bg-slate-50 rounded-xl border border-slate-100 space-y-3">
|
|
<div className="grid grid-cols-2 gap-3 text-xs">
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Entry Permit No</span>
|
|
<span className="font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.entry_permit_no)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">UID No</span>
|
|
<span className="font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.uid_no)}</span>
|
|
</div>
|
|
{visaDoc.ocr_data?.id_number && visaDoc.ocr_data?.id_number !== 'N/A' && (
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">ID Number</span>
|
|
<span className="font-mono font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.id_number)}</span>
|
|
</div>
|
|
)}
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Visa Type</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.visa_type)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Profession</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.profession)}</span>
|
|
</div>
|
|
{sponsorName && (
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Sponsor Name</span>
|
|
<span className="font-bold text-slate-800">{safeRender(sponsorName)}</span>
|
|
</div>
|
|
)}
|
|
{sponsorMobile && (
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Sponsor Mobile</span>
|
|
<span className="font-mono font-bold text-slate-800">{safeRender(sponsorMobile)}</span>
|
|
</div>
|
|
)}
|
|
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Nationality</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.nationality)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Country</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.country)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Date of Birth</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.date_of_birth)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Place of Birth</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.place_of_birth)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Place of Issue</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.place_of_issue)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Issue Date</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.issue_date || visaDoc.issue_date)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Expiry Date</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.expiry_date || visaDoc.expiry_date)}</span>
|
|
</div>
|
|
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">Document Type</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.document_type)}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-wider block">OCR Accuracy</span>
|
|
<span className="font-bold text-slate-800">{safeRender(visaDoc.ocr_data?.ocr_accuracy)}</span>
|
|
</div>
|
|
</div>
|
|
{visaDoc.file_path && (
|
|
<div className="pt-2 border-t border-slate-200/60 flex justify-end">
|
|
<a
|
|
href={`/storage/${visaDoc.file_path}`}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="inline-flex items-center space-x-1 px-3 py-1.5 bg-white border border-slate-200 rounded-lg text-[10px] font-black uppercase tracking-wider text-[#0F6E56] hover:bg-slate-50 transition-colors shadow-sm"
|
|
>
|
|
<FileText className="w-3.5 h-3.5" />
|
|
<span>View Visa File</span>
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
})()}
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/* Right Column (1/3) */}
|
|
<div className="space-y-6">
|
|
{/* Contact Detail */}
|
|
<div className="bg-white p-5 rounded-2xl border border-slate-200 shadow-sm space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Contact Detail</h3>
|
|
<div className="space-y-3.5 text-xs font-bold text-slate-700">
|
|
<div className="flex items-center space-x-2 bg-slate-50 p-2.5 rounded-xl border border-slate-100">
|
|
<Phone className="w-4 h-4 text-teal-600 flex-shrink-0" />
|
|
<span className="text-slate-800 font-extrabold">{selectedWorker?.phone || '+971 52 489 1209'}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Verification Status & Fraud Flag Card */}
|
|
<div className="bg-white p-5 rounded-2xl border border-slate-200 shadow-sm space-y-4">
|
|
<h3 className="text-xs font-black text-slate-400 uppercase tracking-wider">Verification & Trust</h3>
|
|
<div className="space-y-4">
|
|
{/* Verification Action */}
|
|
<div>
|
|
<label className="text-[9px] font-black text-slate-400 uppercase tracking-widest block mb-2">Verification Status</label>
|
|
{selectedWorker?.verified ? (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-1.5 text-emerald-600 bg-emerald-50 border border-emerald-100 px-3 py-2 rounded-xl text-xs font-extrabold">
|
|
<CheckCircle2 className="w-4 h-4" />
|
|
<span>Verified Candidate</span>
|
|
</div>
|
|
<button
|
|
onClick={() => handleVerifyAction(selectedWorker.id, 'reject')}
|
|
className="w-full py-2 bg-slate-100 hover:bg-slate-200 text-slate-600 rounded-xl text-[10px] font-black uppercase tracking-wider transition-all"
|
|
>
|
|
Revert to Pending
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center space-x-1.5 text-amber-600 bg-amber-50 border border-amber-100 px-3 py-2 rounded-xl text-xs font-extrabold">
|
|
<AlertTriangle className="w-4 h-4" />
|
|
<span>Pending Verification</span>
|
|
</div>
|
|
<button
|
|
onClick={() => handleVerifyAction(selectedWorker.id, 'approve')}
|
|
className="w-full py-2 bg-[#0F6E56] hover:bg-[#085041] text-white rounded-xl text-[10px] font-black uppercase tracking-wider transition-all shadow-md shadow-[#0F6E56]/10"
|
|
>
|
|
Approve Profile
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
{/* Bottom controls */}
|
|
<div className="p-6 border-t border-slate-100 bg-slate-50 flex items-center justify-between flex-shrink-0">
|
|
<div className="flex items-center space-x-2">
|
|
<span className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Account State:</span>
|
|
<div className="flex bg-slate-200 p-1 rounded-xl">
|
|
{['active', 'suspended', 'banned'].map(st => (
|
|
<button
|
|
key={st}
|
|
onClick={() => handleToggleStatus(selectedWorker.id, st)}
|
|
className={`px-3 py-1 rounded-lg text-[9px] font-black uppercase tracking-wider transition-all ${selectedWorker?.status === st
|
|
? 'bg-white text-slate-800 shadow-sm'
|
|
: 'text-slate-500 hover:text-slate-900'
|
|
}`}
|
|
>
|
|
{st}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
{isEditMode ? (
|
|
<>
|
|
<button
|
|
onClick={handleProfileSubmit}
|
|
className="px-6 py-2.5 bg-[#0F6E56] hover:bg-[#085041] text-white rounded-xl text-[10px] font-black uppercase tracking-widest transition-all"
|
|
>
|
|
Save Changes
|
|
</button>
|
|
<button
|
|
onClick={() => setIsEditMode(false)}
|
|
className="px-6 py-2.5 bg-slate-200 hover:bg-slate-300 text-slate-700 rounded-xl text-[10px] font-black uppercase tracking-widest transition-all"
|
|
>
|
|
Cancel
|
|
</button>
|
|
</>
|
|
) : (
|
|
<button
|
|
onClick={() => setIsEditMode(true)}
|
|
className="px-6 py-2.5 bg-[#0F6E56] hover:bg-[#085041] text-white rounded-xl text-[10px] font-black uppercase tracking-widest transition-all"
|
|
>
|
|
Edit Profile
|
|
</button>
|
|
)}
|
|
<button
|
|
onClick={() => setIsDetailDialogOpen(false)}
|
|
className="px-6 py-2.5 bg-slate-950 hover:bg-slate-900 text-white rounded-xl text-[10px] font-black uppercase tracking-widest transition-all"
|
|
>
|
|
Close Controls
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</AdminLayout>
|
|
);
|
|
}
|