Bienvenue sur WordPress. Ceci est votre premier article. Modifiez-le ou supprimez-le, puis commencez à écrire !
import React, { useState, useMemo } from 'react';
import { User, JobOffer, JobStatus, UserRole, AdminLog, Language } from '../types';
import TwoFactorSetup from '../components/TwoFactorSetup';
import { useAppStore } from '../store/useAppStore';
import { translations } from '../i18n';
interface AdminDashboardProps {
user: User; // L'administrateur connecté
jobs: JobOffer[]; // Liste globale des offres pour modération
allUsers: User[]; // Liste globale des utilisateurs pour gestion
logs: AdminLog[]; // Journal d'audit (actions effectuées sur le système)
pendingRecruiters: User[]; // Nouveaux recruteurs en attente d'approbation
onPublish: (jobId: string) => void; // Approuver une offre
onRejectJob: (jobId: string, reason: string) => void; // Rejeter une offre avec motif
onCloseJob: (jobId: string) => void; // Fermer une offre manuellement
onDeleteJob: (jobId: string) => void; // Supprimer une offre
onReopenJob: (jobId: string) => void; // Réouvrir une offre clôturée
onApproveRecruiter: (userId: string) => void; // Valider l'inscription d'un recruteur
onRejectRecruiter: (userId: string, reason: string) => void; // Refuser un recruteur avec motif
onDeleteUser: (userId: string) => void; // Supprimer un compte utilisateur
onToggleStatus: (userId: string) => void; // Activer/Désactiver un compte
onChangeRole: (userId: string, newRole: UserRole) => void; // Changer les permissions
onAddPartner: (partnerData: any) => void; // Ajouter manuellement un partenaire
onLogout: () => void; // Déconnexion
}
/**
* TABLEAU DE BORD ADMINISTRATEUR
* Centre de contrôle suprême de l'application MatchJob.
* Permet la gestion des utilisateurs, la modération des offres, et l'audit système.
*/
const AdminDashboard: React.FC<AdminDashboardProps> = ({
user, jobs, allUsers, logs, pendingRecruiters,
onPublish, onRejectJob, onCloseJob, onDeleteJob, onReopenJob,
onApproveRecruiter, onRejectRecruiter, onDeleteUser, onToggleStatus, onChangeRole,
onAddPartner, onLogout
}) => {
const { language } = useAppStore();
const t = translations[language];
// --- ÉTATS DE NAVIGATION ---
const [activeTab, setActiveTab] = useState<'STATS' | 'USERS' | 'MODERATION' | 'PARTNERS' | 'OFFRES' | 'LOGS' | 'SECURITY'>('STATS');
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); // État du menu mobile (Burger)
// --- ÉTATS D'INSPECTION (MODALES) ---
const [inspectingJob, setInspectingJob] = useState<JobOffer | null>(null); // Détail d'une offre pour modération
const [inspectingUser, setInspectingUser] = useState<User | null>(null); // Détail d'un utilisateur standard
const [inspectingRecruiter, setInspectingRecruiter] = useState<User | null>(null); // Détail d'un nouveau recruteur
const [rejectionReason, setRejectionReason] = useState(''); // Motif de refus (Job ou Recruteur)
const [jobStatusFilter, setJobStatusFilter] = useState<'ALL' | JobStatus>('ALL');
const sidebarItems = [
{ id: 'STATS', label: t.dashboardTab, icon: 'fa-chart-pie' },
{ id: 'USERS', label: t.usersTab, icon: 'fa-users' },
{ id: 'OFFRES', label: t.offersTab, icon: 'fa-briefcase' },
{ id: 'MODERATION', label: t.alertsTab, icon: 'fa-shield-halved' },
{ id: 'PARTNERS', label: t.partnersTab, icon: 'fa-handshake' },
{ id: 'LOGS', label: t.auditLogsTab, icon: 'fa-clock-rotate-left' }
];
// --- ÉTATS PARTENAIRES ---
const [isAddingPartner, setIsAddingPartner] = useState(false);
const [newPartner, setNewPartner] = useState({ company: '', email: '', name: '' });
// --- FILTRES ET CALCULS MÉMOÏSÉS ---
// Offres en attente de validation
const pendingJobs = jobs.filter(j => j.status === JobStatus.PENDING_VALIDATION);
// Recruteurs déjà approuvés
const approvedPartners = useMemo(() =>
allUsers.filter(u => u.role === UserRole.RECRUITER && u.isApproved === true),
[allUsers]
);
// Filtrage des offres par statut
const filteredJobsByStatus = useMemo(() => {
if (jobStatusFilter === 'ALL') return jobs;
return jobs.filter(j => j.status === jobStatusFilter);
}, [jobs, jobStatusFilter]);
// --- FONCTIONS DE MANIPULATION ---
/**
* Publier une offre en attente
*/
const handlePublish = async (id: string) => {
if (!id) return;
try {
await onPublish(id);
setInspectingJob(null);
} catch (err) {
console.error("Erreur publication:", err);
}
};
/**
* Publier toutes les offres en attente
*/
const handlePublishAllJobs = async () => {
if (pendingJobs.length === 0) return;
if (window.confirm(language === Language.AR ? `هل تريد فعلاً الموافقة على ونشر ${pendingJobs.length} عرضاً معلقاً؟` : `Voulez-vous vraiment valider et publier les ${pendingJobs.length} offres en attente ?`)) {
try {
for (const job of pendingJobs) {
await onPublish(job.id);
}
} catch (err) {
console.error("Erreur lors de la validation groupée:", err);
}
}
};
/**
* Rejeter une offre en attente
*/
const handleReject = async (id: string) => {
if (!id) return;
try {
await onRejectJob(id, 'Contenu non conforme aux standards MatchJob');
setInspectingJob(null);
} catch (err) {
console.error("Erreur rejet:", err);
}
};
/**
* Ajouter manuellement un nouveau partenaire entreprise
*/
const submitNewPartner = (e: React.FormEvent) => {
e.preventDefault();
onAddPartner(newPartner);
setIsAddingPartner(false);
setNewPartner({ company: '', email: '', name: '' });
};
const Sidebar = () => (
<>
{/* Overlay pour fermer le menu mobile en cliquant à côté */}
{isMobileMenuOpen && (
<div
className="fixed inset-0 bg-black/50 z-[100] lg:hidden backdrop-blur-sm"
onClick={() => setIsMobileMenuOpen(false)}
></div>
)}
<div className={`
w-80 bg-[#001e3c] h-screen flex flex-col p-8 fixed top-0 text-white z-40
overflow-y-auto custom-scrollbar border-r border-white/5 transition-transform duration-300 ease-in-out
${language === Language.AR
? (isMobileMenuOpen ? 'right-0 translate-x-0' : 'right-0 translate-x-full lg:translate-x-0')
: (isMobileMenuOpen ? 'left-0 translate-x-0' : 'left-0 -translate-x-full lg:translate-x-0')}
`}>
{/* En-tête du menu (Logo ou Nom) */}
<div className="mb-10 px-4">
<h2 className="text-xl font-black tracking-widest text-blue-400 uppercase">MatchJob <span className="text-white text-[10px] block opacity-50 tracking-normal normal-case font-medium">Administration System</span></h2>
</div>
{/* Menu Principal */}
<nav className="flex-grow space-y-3">
{[
{ id: 'STATS', label: t.dashboardTab, icon: 'fa-chart-pie' },
{ id: 'USERS', label: t.usersTab, icon: 'fa-users' },
{ id: 'OFFRES', label: t.offersTab, icon: 'fa-briefcase' },
{ id: 'MODERATION', label: t.alertsTab, icon: 'fa-shield-halved' },
{ id: 'PARTNERS', label: t.partnersTab, icon: 'fa-handshake' },
{ id: 'LOGS', label: t.auditLogsTab, icon: 'fa-clock-rotate-left' }
].map(item => (
<button
key={item.id}
onClick={() => {
setActiveTab(item.id as any);
setIsMobileMenuOpen(false); // Fermer le menu après clic sur mobile
}}
className={`w-full flex items-center gap-4 px-5 py-4 rounded-2xl font-bold text-[13px] transition-all ${activeTab === item.id ? 'bg-blue-600 text-white shadow-xl shadow-blue-500/20' : 'text-slate-400 hover:text-white hover:bg-white/5'}`}
>
<i className={`fas ${item.icon} w-5 text-center text-lg`}></i> {item.label}
</button>
))}
</nav>
</div>
</>
);
return (
<div className={`min-h-screen bg-[#f4f7fa] flex transition-all duration-300 ${language === Language.AR ? 'lg:pr-80 font-arabic' : 'lg:pl-80'}`}>
<Sidebar />
<main className="flex-grow p-4 md:p-8 lg:p-12 space-y-8 md:space-y-12 animate-in fade-in duration-700 w-full max-w-full overflow-x-hidden">
<div className="flex justify-between items-center gap-4">
<div className="flex items-center gap-4">
{/* Bouton Burger (Menu Mobile) */}
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="lg:hidden w-12 h-12 bg-white rounded-xl shadow-sm border border-slate-100 flex items-center justify-center text-[#003366] active:scale-95 transition-all"
>
<i className={`fas ${isMobileMenuOpen ? 'fa-times' : 'fa-bars'} text-xl`}></i>
</button>
<h1 className={`text-2xl md:text-4xl font-black text-[#003366] tracking-tight ${language === Language.AR ? 'font-arabic' : ''}`}>
{language === Language.AR ? 'فضاء' : 'Espace'} <span className="text-[#0056b3]">{t.adminPortal.split(' ').pop()}</span>
</h1>
</div>
</div>
{/* VUE : STATISTIQUES RAPIDES */}
{activeTab === 'STATS' && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{/* Nombre total d'utilisateurs */}
<div className="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-sm">
<div className="w-12 h-12 bg-blue-50 text-blue-600 rounded-xl flex items-center justify-center text-xl mb-4"><i className="fas fa-users"></i></div>
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Utilisateurs</p>
<h3 className="text-3xl font-black text-slate-900">{allUsers.length}</h3>
</div>
{/* Nombre d'offres actuellement en ligne */}
<div className="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-sm">
<div className="w-12 h-12 bg-green-50 text-green-600 rounded-xl flex items-center justify-center text-xl mb-4"><i className="fas fa-check-circle"></i></div>
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Offres Publiées</p>
<h3 className="text-3xl font-black text-slate-900">{jobs.filter(j => j.status === JobStatus.PUBLISHED).length}</h3>
</div>
{/* Nombre d'éléments demandant une action de modération */}
<div onClick={() => setActiveTab('MODERATION')} className="bg-white p-8 rounded-[2rem] border border-slate-100 shadow-sm cursor-pointer hover:shadow-md transition-all hover:scale-[1.02] active:scale-95 group">
<div className="w-12 h-12 bg-red-50 text-red-600 rounded-xl flex items-center justify-center text-xl mb-4 group-hover:bg-red-500 group-hover:text-white transition-all"><i className="fas fa-shield-halved"></i></div>
<p className={`text-[10px] font-black text-slate-400 uppercase tracking-widest ${language === Language.AR ? 'font-arabic' : ''}`}>{t.alertsTab} Moderation</p>
<h3 className="text-3xl font-black text-slate-900">{pendingJobs.length + pendingRecruiters.length}</h3>
</div>
{/* Nombre d'entreprises partenaires validées */}
<div className="bg-[#001f3f] p-8 rounded-[2rem] shadow-xl text-white">
<div className="w-12 h-12 bg-white/10 rounded-xl flex items-center justify-center text-xl mb-4"><i className="fas fa-handshake"></i></div>
<p className="text-[10px] font-black text-blue-200/50 uppercase tracking-widest">Partenaires</p>
<h3 className="text-3xl font-black">{approvedPartners.length}</h3>
</div>
</div>
)}
{/* VUE : JOURNAL D'AUDIT (LOGS) */}
{activeTab === 'LOGS' && (
<div className="bg-white rounded-[2.5rem] shadow-sm border border-slate-100 overflow-hidden animate-in slide-in-from-bottom-4">
<div className={`p-8 border-b border-slate-50 flex justify-between items-center bg-slate-50/50 ${language === Language.AR ? 'flex-row-reverse' : ''}`}>
<h2 className={`text-xl font-black text-slate-800 uppercase tracking-tight ${language === Language.AR ? 'font-arabic' : ''}`}>{t.auditJournalTitle}</h2>
<span className={`text-[10px] font-black text-slate-400 uppercase tracking-widest ${language === Language.AR ? 'font-arabic' : ''}`}>{t.realTimeHistory}</span>
</div>
<div className="p-8 space-y-4 max-h-[600px] overflow-y-auto">
{logs && logs.length > 0 ? logs.map(log => (
<div key={log.id} className="flex gap-4 p-4 hover:bg-slate-50 rounded-2xl transition border-l-4 border-slate-100 hover:border-blue-500">
{/* Icône changeante selon le type d'action (Sécurité, Job, User) */}
<div className={`w-10 h-10 rounded-lg flex items-center justify-center shrink-0 ${log.type === 'SECURITY' ? 'bg-red-50 text-red-500' : 'bg-slate-100 text-slate-500'}`}>
<i className={`fas ${log.type === 'JOB' ? 'fa-briefcase' : log.type === 'SECURITY' ? 'fa-shield-halved' : 'fa-user'}`}></i>
</div>
<div>
<div className="flex items-center gap-3">
<p className="font-black text-slate-900 text-sm">{log.action}</p>
<span className="text-[10px] font-mono text-slate-400">{new Date(log.timestamp).toLocaleString()}</span>
</div>
<p className="text-xs text-slate-500 mt-1">
<span className="font-bold text-blue-600">{log.adminName}</span> sur <span className="text-slate-900">{log.target}</span> : {log.details}
</p>
</div>
</div>
)) : (
<div className="py-20 text-center space-y-4">
<div className="w-16 h-16 bg-slate-50 text-slate-200 rounded-full flex items-center justify-center mx-auto text-2xl">
<i className="fas fa-clock-rotate-left"></i>
</div>
<p className={`text-slate-400 font-bold italic ${language === Language.AR ? 'font-arabic' : ''}`}>{t.noActivity}</p>
</div>
)}
</div>
</div>
)}
{/* VUE : GESTION DES UTILISATEURS */}
{activeTab === 'USERS' && (
<div className="bg-white rounded-[2.5rem] shadow-sm border border-slate-100 overflow-hidden">
<table className="w-full text-left">
<thead className={`bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100 ${language === Language.AR ? 'text-right' : ''}`}>
<tr>
<th className="px-8 py-6">{t.userColumn}</th>
<th className="px-8 py-6">{t.roleColumn}</th>
<th className={`px-8 py-6 ${language === Language.AR ? 'text-left' : 'text-right'}`}>{t.actionsColumn}</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{allUsers.map(u => (
<tr key={u.id} className="hover:bg-slate-50 transition">
<td className="px-8 py-6">
<p className="font-black text-slate-900">{u.name}</p>
<p className="text-xs text-slate-400">{u.email}</p>
</td>
<td className="px-8 py-6">
<span className="bg-blue-50 text-blue-600 px-3 py-1 rounded-lg text-[10px] font-black uppercase tracking-widest">{u.role}</span>
</td>
<td className="px-8 py-6 text-right space-x-2">
<button onClick={() => setInspectingUser(u)} className="w-9 h-9 rounded-xl bg-blue-50 text-blue-500 hover:bg-blue-500 hover:text-white transition">
<i className="fas fa-eye"></i>
</button>
<button onClick={() => onToggleStatus(u.id)} className={`w-9 h-9 rounded-xl transition ${u.isActive ? 'bg-slate-100 text-slate-400 hover:text-red-500' : 'bg-green-50 text-green-600'}`}>
<i className={`fas ${u.isActive ? 'fa-user-slash' : 'fa-user-check'}`}></i>
</button>
<button onClick={() => onDeleteUser(u.id)} className="w-9 h-9 rounded-xl bg-red-50 text-red-500 hover:bg-red-500 hover:text-white transition"><i className="fas fa-trash-can"></i></button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{/* VUE : GESTION DES OFFRES D'EMPLOI */}
{activeTab === 'OFFRES' && (
<div className="bg-white rounded-[2.5rem] shadow-sm border border-slate-100 overflow-hidden">
<div className={`p-8 border-b border-slate-50 flex justify-between items-center bg-slate-50/50 ${language === Language.AR ? 'flex-row-reverse' : ''}`}>
<h2 className={`text-xl font-black text-slate-800 uppercase tracking-tight ${language === Language.AR ? 'font-arabic' : ''}`}>{t.offersTab}</h2>
<div className={`flex items-center gap-4 ${language === Language.AR ? 'flex-row-reverse' : ''}`}>
<span className={`text-[10px] font-black text-slate-400 uppercase tracking-widest ${language === Language.AR ? 'font-arabic' : ''}`}>{t.filterByStatus} :</span>
<select
value={jobStatusFilter}
onChange={(e) => setJobStatusFilter(e.target.value as any)}
className={`bg-white border border-slate-200 rounded-xl px-4 py-2 text-[10px] font-black uppercase tracking-widest outline-none focus:border-blue-500 transition-all cursor-pointer ${language === Language.AR ? 'text-right' : ''}`}
>
<option value="ALL">{t.allOffers}</option>
<option value={JobStatus.PENDING_VALIDATION}>{t.pendingStatus}</option>
<option value={JobStatus.PUBLISHED}>{t.publishedStatus}</option>
<option value={JobStatus.REJECTED}>{t.rejectedStatus}</option>
<option value={JobStatus.CLOSED}>{t.closedStatus}</option>
<option value={JobStatus.ARCHIVED}>{t.jobStatusArchived}</option>
</select>
</div>
</div>
<table className="w-full text-left">
<thead className={`bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100 ${language === Language.AR ? 'text-right' : ''}`}>
<tr>
<th className="px-8 py-6">{t.offersTab.split('&')[0].trim()}</th>
<th className="px-8 py-6">{(t as any).companies}</th>
<th className="px-8 py-6">Contrat</th>
<th className="px-8 py-6">Statut</th>
<th className={`px-8 py-6 ${language === Language.AR ? 'text-left' : 'text-right'}`}>{t.actionsColumn}</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{filteredJobsByStatus.map(job => (
<tr key={job.id} className={`hover:bg-slate-50 transition ${job.status === JobStatus.ARCHIVED ? 'opacity-50' : ''}`}>
<td className="px-8 py-6 font-black text-slate-900">{job.title}</td>
<td className="px-8 py-6 text-sm font-bold text-slate-400">{job.company}</td>
<td className="px-8 py-6">
<span className="bg-blue-50 text-blue-600 px-3 py-1 rounded text-[9px] font-black uppercase">{job.contractType || 'CDI'}</span>
</td>
<td className="px-8 py-6">
<span className={`px-3 py-1 rounded-lg text-[9px] font-black uppercase whitespace-nowrap ${job.status === JobStatus.PUBLISHED ? 'bg-emerald-50 text-emerald-600' :
job.status === JobStatus.PENDING_VALIDATION ? 'bg-amber-50 text-amber-600' :
job.status === JobStatus.REJECTED ? 'bg-rose-50 text-rose-600' :
job.status === JobStatus.CLOSED ? 'bg-slate-100 text-slate-600' :
'bg-slate-50 text-slate-400'
}`}>
{job.status === JobStatus.PUBLISHED ? t.publishedStatus :
job.status === JobStatus.PENDING_VALIDATION ? t.pendingStatus :
job.status === JobStatus.REJECTED ? t.rejectedStatus :
job.status === JobStatus.CLOSED ? t.closedStatus : job.status}
</span>
</td>
<td className="px-8 py-6 text-right space-x-2">
<button onClick={() => setInspectingJob(job)} className="w-9 h-9 rounded-xl bg-blue-50 text-blue-500 hover:bg-blue-500 hover:text-white transition"><i className="fas fa-eye"></i></button>
<button onClick={() => onDeleteJob(job.id)} className="w-9 h-9 rounded-xl bg-red-50 text-red-500 hover:bg-red-500 hover:text-white transition"><i className="fas fa-trash-can"></i></button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{/* VUE : PARTENAIRES (RECRUTEURS APPROUVÉS) */}
{activeTab === 'PARTNERS' && (
<div className="bg-white rounded-[2.5rem] shadow-sm border border-slate-100 overflow-hidden">
<div className="p-8 border-b border-slate-50 bg-[#001f3f] text-white flex justify-between items-center">
<h2 className="text-xl font-black">Entreprises Partenaires MatchJob</h2>
<button
onClick={() => setIsAddingPartner(true)}
className={`bg-[#0056b3] text-white px-6 py-2.5 rounded-xl text-[11px] font-black uppercase tracking-widest shadow-lg hover:bg-blue-600 transition ${language === Language.AR ? 'font-arabic' : ''}`}
>
<i className="fas fa-plus mr-2"></i> {t.addPartner}
</button>
</div>
<table className="w-full text-left">
<thead className={`bg-slate-50 text-[10px] font-black text-slate-400 uppercase tracking-widest border-b border-slate-100 ${language === Language.AR ? 'text-right' : ''}`}>
<tr>
<th className="px-10 py-6">{(t as any).companies}</th>
<th className="px-10 py-6">Contact</th>
<th className={`px-10 py-6 ${language === Language.AR ? 'text-left' : 'text-right'}`}>{t.actionsColumn}</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{approvedPartners.map(u => (
<tr key={u.id} className="hover:bg-slate-50 transition">
<td className="px-10 py-6 font-black text-slate-900 text-lg tracking-tight">{u.recruiterProfile?.companyName || u.company || 'Sans nom'}</td>
<td className="px-10 py-6 text-sm text-slate-400">{u.email}</td>
<td className="px-10 py-6 text-right">
<button onClick={() => onToggleStatus(u.id)} className="text-slate-300 hover:text-red-500 transition"><i className="fas fa-ban"></i></button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{/* VUE : MODÉRATION (ÉLÉMENTS EN ATTENTE DE VALIDATION) */}
{activeTab === 'MODERATION' && (
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="p-8">
{/* Cas où tout est traité */}
{pendingRecruiters.length === 0 && pendingJobs.length === 0 && (
<p className="text-center py-10 italic text-slate-400 font-medium">Aucune demande en attente.</p>
)}
{/* LISTE DES NOUVEAUX RECRUTEURS À APPROUVER */}
{pendingRecruiters.length > 0 && (
<div className="mb-10">
<h3 className="text-xs font-black text-slate-400 uppercase tracking-widest mb-6 px-2"><i className="fas fa-building mr-2"></i>Recruteurs en attente d'approbation</h3>
{pendingRecruiters.map(pr => (
<div key={pr.id} className="flex items-center justify-between p-6 bg-slate-50 rounded-2xl mb-4 group hover:bg-white hover:shadow-md transition-all border border-transparent hover:border-slate-100">
<div className="flex gap-4 items-center">
<div className="w-12 h-12 bg-amber-100 text-amber-600 rounded-xl flex items-center justify-center text-lg">
<i className="fas fa-building"></i>
</div>
<div>
<p className="font-black text-slate-900">{pr.recruiterProfile?.companyName || pr.company || 'Sans nom'}</p>
<p className="text-xs text-slate-400">{pr.email}</p>
</div>
</div>
<div className="flex gap-3">
<button
onClick={() => setInspectingRecruiter(pr)}
className="bg-white text-slate-600 px-6 py-2 rounded-xl text-[10px] font-black uppercase tracking-widest border border-slate-200 hover:bg-slate-50 transition"
>
Détails
</button>
<button
onClick={() => onApproveRecruiter(pr.id)}
className="bg-emerald-500 text-white px-6 py-2 rounded-xl text-[10px] font-black uppercase tracking-widest shadow-md hover:bg-emerald-600 transition"
>
Approuver
</button>
</div>
</div>
))}
</div>
)}
{/* LISTE DES NOUVELLES OFFRES À PUBLIER */}
{pendingJobs.length > 0 && (
<div className="mb-10">
<div className="flex justify-between items-center mb-6 px-2">
<h3 className="text-xs font-black text-slate-400 uppercase tracking-widest"><i className="fas fa-briefcase mr-2"></i>Offres d'emploi en attente de publication</h3>
<button
onClick={handlePublishAllJobs}
className="bg-emerald-500 text-white px-5 py-2.5 rounded-xl text-[10px] font-black uppercase tracking-widest shadow-lg shadow-emerald-100 hover:bg-emerald-600 transition-all flex items-center gap-2"
>
<i className="fas fa-check-double"></i> {language === Language.AR ? 'الموافقة على الكل' : 'Tout valider'}
</button>
</div>
{pendingJobs.map(job => (
<div key={job.id} className="flex items-center justify-between p-6 bg-slate-50 rounded-2xl mb-4 group hover:bg-white hover:shadow-md transition-all border border-transparent hover:border-slate-100">
<div className="flex gap-4 items-center">
<div className="w-12 h-12 bg-blue-100 text-blue-600 rounded-xl flex items-center justify-center text-lg">
<i className="fas fa-briefcase"></i>
</div>
<div>
<p className="font-black text-slate-900">{job.title}</p>
<p className="text-xs text-slate-400">{job.company}</p>
</div>
</div>
<div className="flex gap-3">
<button
onClick={() => setInspectingJob(job)}
className="bg-white text-slate-600 px-6 py-2 rounded-xl text-[10px] font-black uppercase tracking-widest border border-slate-200 hover:bg-slate-50 transition"
>
Détails
</button>
<button
onClick={() => handlePublish(job.id)}
className="bg-emerald-500 text-white px-6 py-2 rounded-xl text-[10px] font-black uppercase tracking-widest shadow-md hover:bg-emerald-600 transition"
>
Publier
</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
)}
{/* MODAL : INSPECTION ET VALIDATION D'UN RECRUTEUR */}
{
inspectingRecruiter && (
<div className={`fixed inset-0 ${language === Language.AR ? 'lg:pr-80' : 'lg:pl-80'} bg-slate-900/40 backdrop-blur-sm z-[1000] flex items-center justify-center p-6`}>
<div className="bg-white w-full max-w-2xl rounded-[3rem] p-10 overflow-y-auto max-h-[95vh] shadow-2xl animate-in zoom-in-95 duration-200 relative">
{/* Entête de la modale */}
<div className="flex justify-between items-start mb-10">
<div className="flex gap-5 items-center">
<div className="w-16 h-16 bg-amber-50 text-amber-500 rounded-2xl flex items-center justify-center text-2xl shadow-sm border border-amber-100">
<i className="fas fa-building-circle-check"></i>
</div>
<div>
<h2 className="text-3xl font-black text-slate-900 tracking-tight">Demande d'adhésion</h2>
<p className="text-amber-600 font-black uppercase text-[10px] tracking-[0.2em] mt-1">Dossier en attente de validation</p>
</div>
</div>
<button onClick={() => { setInspectingRecruiter(null); setRejectionReason(''); }} className="text-slate-300 hover:text-red-500 transition-colors bg-slate-50 w-10 h-10 rounded-full flex items-center justify-center"><i className="fas fa-times text-xl"></i></button>
</div>
<div className="grid grid-cols-2 gap-6 mb-10">
{/* Détails légaux de l'entreprise */}
<div className="bg-slate-50 p-6 rounded-3xl border border-slate-100">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3 px-1">Informations Entreprise</p>
<div className="space-y-4">
<div>
<label className="text-[9px] font-black text-slate-300 uppercase block mb-1">Nom / Enseigne</label>
<p className="font-black text-slate-800 text-lg leading-tight">{inspectingRecruiter.recruiterProfile?.companyName || inspectingRecruiter.company}</p>
</div>
<div>
<label className="text-[9px] font-black text-slate-300 uppercase block mb-1">Matricule Fiscal</label>
<p className="font-bold text-slate-600">{inspectingRecruiter.recruiterProfile?.taxId || 'Non fourni'}</p>
</div>
<div>
<label className="text-[9px] font-black text-slate-300 uppercase block mb-1">Site Web</label>
<a href={inspectingRecruiter.recruiterProfile?.website} target="_blank" rel="noreferrer" className="text-blue-600 font-bold hover:underline truncate block">
{inspectingRecruiter.recruiterProfile?.website || 'Lien non disponible'}
</a>
</div>
</div>
</div>
{/* Coordonnées de la personne de contact */}
<div className="bg-slate-50 p-6 rounded-3xl border border-slate-100">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3 px-1">Contact Référent</p>
<div className="space-y-4">
<div>
<label className="text-[9px] font-black text-slate-300 uppercase block mb-1">Nom du Contact</label>
<p className="font-black text-slate-800 text-lg leading-tight">{inspectingRecruiter.name}</p>
</div>
<div>
<label className="text-[9px] font-black text-slate-300 uppercase block mb-1">Email Professionnel</label>
<p className="font-bold text-slate-600">{inspectingRecruiter.email}</p>
</div>
<div>
<label className="text-[9px] font-black text-slate-300 uppercase block mb-1">Rôle</label>
<span className="bg-amber-100 text-amber-700 px-2.5 py-1 rounded text-[9px] font-black uppercase">Recruteur</span>
</div>
</div>
</div>
</div>
{/* Zone de décision (Validation / Refus) */}
<div className="space-y-6 mb-10">
<div className="p-6 bg-blue-50/50 rounded-2xl border border-blue-100 flex gap-4 items-center">
<i className="fas fa-circle-info text-blue-500 text-xl"></i>
<p className="text-xs text-blue-800 leading-relaxed font-bold uppercase tracking-tight">
L'approbation enverra automatiquement un email de bienvenue et activera le compte. Le refus demande un motif qui sera envoyé par email.
</p>
</div>
<div className="space-y-3">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest px-1">Motif de refus (sera envoyé au recruteur)</label>
<textarea
className="w-full bg-slate-50 border-2 border-transparent p-5 rounded-2xl focus:bg-white focus:border-red-400 transition-all outline-none font-bold text-slate-700 text-sm h-28"
placeholder="Expliquez pourquoi le dossier est rejeté (ex: Matricule invalide, Site web inaccessible...)"
value={rejectionReason}
onChange={(e) => setRejectionReason(e.target.value)}
/>
</div>
</div>
<div className="flex gap-4 pt-6 border-t border-slate-100">
<button
onClick={() => { onApproveRecruiter(inspectingRecruiter.id); setInspectingRecruiter(null); }}
className="flex-3 bg-emerald-500 text-white px-10 py-5 rounded-[1.5rem] font-black uppercase text-xs tracking-[0.1em] shadow-[0_10px_25px_rgba(16,185,129,0.3)] hover:scale-[1.02] active:scale-95 transition-all flex-1"
>
Valider & Approuver
</button>
<button
onClick={() => { onRejectRecruiter(inspectingRecruiter.id, rejectionReason || 'Votre dossier ne remplit pas nos conditions actuelles.'); setInspectingRecruiter(null); }}
className="flex-1 bg-red-50 text-red-500 hover:bg-red-500 hover:text-white px-8 py-5 rounded-[1.5rem] font-black uppercase text-xs tracking-[0.1em] transition-all active:scale-95"
>
Refuser le dossier
</button>
</div>
</div>
</div>
)
}
{/* MODAL : AJOUT PARTENAIRE */}
{isAddingPartner && (
<div className={`fixed inset-0 ${language === Language.AR ? 'lg:pr-80' : 'lg:pl-80'} bg-slate-900/40 backdrop-blur-sm z-[1000] flex items-center justify-center p-6`}>
<div className="bg-white w-full max-w-lg rounded-[2.5rem] p-10 shadow-3xl animate-in zoom-in-95 duration-200">
<div className="flex justify-between items-center mb-8">
<h2 className="text-2xl font-black text-slate-900">Nouvelle Entreprise</h2>
<button onClick={() => setIsAddingPartner(false)} className="text-slate-300 hover:text-slate-600"><i className="fas fa-times text-xl"></i></button>
</div>
<form onSubmit={submitNewPartner} className="space-y-5">
<div>
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2 px-1">Nom de l'entreprise</label>
<input
required type="text"
placeholder="Nom de la société"
className="w-full bg-slate-50 p-4 rounded-xl border-2 border-transparent focus:border-[#0056b3] focus:bg-white transition outline-none font-bold"
value={newPartner.company}
onChange={e => setNewPartner({ ...newPartner, company: e.target.value })}
/>
</div>
<div>
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2 px-1">Nom du contact</label>
<input
required type="text"
placeholder="Prénom et Nom"
className="w-full bg-slate-50 p-4 rounded-xl border-2 border-transparent focus:border-[#0056b3] focus:bg-white transition outline-none font-bold"
value={newPartner.name}
onChange={e => setNewPartner({ ...newPartner, name: e.target.value })}
/>
</div>
<div>
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest block mb-2 px-1">Email du contact (Recevra les accès)</label>
<input
required type="email"
placeholder="contact@exemple.tn"
className="w-full bg-slate-50 p-4 rounded-xl border-2 border-transparent focus:border-[#0056b3] focus:bg-white transition outline-none font-bold"
value={newPartner.email}
onChange={e => setNewPartner({ ...newPartner, email: e.target.value })}
/>
</div>
<button className="w-full bg-[#0056b3] text-white py-4 rounded-xl font-black uppercase text-xs tracking-widest shadow-xl hover:bg-blue-600 transition">
Générer les accès Partenaire
</button>
</form>
</div>
</div>
)
}
{/* MODAL : INSPECTION OFFRE */}
{inspectingJob && (
<div className={`fixed inset-0 ${language === Language.AR ? 'lg:pr-80' : 'lg:pl-80'} bg-slate-900/40 backdrop-blur-sm z-[1000] flex items-center justify-center p-6`}>
<div className="bg-white w-full max-w-2xl rounded-[3rem] p-10 overflow-y-auto max-h-[90vh] shadow-2xl animate-in zoom-in-95 duration-200">
{/* Titre et Entreprise */}
<div className="flex justify-between items-start mb-8">
<div>
<h2 className="text-3xl font-black text-slate-900 tracking-tight">{inspectingJob.title}</h2>
<p className="text-blue-600 font-black uppercase text-xs tracking-widest mt-1">{inspectingJob.company}</p>
</div>
<button onClick={() => setInspectingJob(null)} className="text-slate-300 hover:text-slate-600 transition-colors"><i className="fas fa-times text-2xl"></i></button>
</div>
{/* Grid d'infos rapides */}
<div className="grid grid-cols-3 gap-4 mb-8">
<div className="bg-slate-50 p-4 rounded-2xl border border-slate-100">
<p className="text-[10px] font-black text-slate-400 uppercase mb-1">Contrat</p>
<p className="font-bold text-slate-900">{inspectingJob.contractType || 'CDI'}</p>
</div>
<div className="bg-slate-50 p-4 rounded-2xl border border-slate-100">
<p className="text-[10px] font-black text-slate-400 uppercase mb-1">Salaire</p>
<p className="font-bold text-slate-900">{inspectingJob.salaryRange || 'N/C'}</p>
</div>
<div className="bg-slate-50 p-4 rounded-2xl border border-slate-100">
<p className="text-[10px] font-black text-slate-400 uppercase mb-1">Localisation</p>
<p className="font-bold text-slate-900">{inspectingJob.location}</p>
</div>
</div>
{/* Description et Compétences */}
<div className="space-y-6 mb-12">
<div>
<h4 className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3">Description du poste</h4>
<p className="text-sm text-slate-600 leading-relaxed font-medium">{inspectingJob.description}</p>
</div>
<div>
<h4 className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3">Compétences clés</h4>
<div className="flex flex-wrap gap-2">
{inspectingJob.requirements.map(r => <span key={r} className="px-3 py-1.5 bg-blue-50 text-blue-600 text-[10px] font-black rounded-lg border border-blue-100 uppercase">#{r}</span>)}
</div>
</div>
</div>
{/* Actions de modération (uniquement si en attente) */}
{inspectingJob.status === JobStatus.PENDING_VALIDATION && (
<div className="flex gap-4 pt-4 border-t border-slate-50">
<button
onClick={(e) => { e.preventDefault(); e.stopPropagation(); handlePublish(inspectingJob.id || (inspectingJob as any)._id); }}
className="flex-1 bg-gradient-to-r from-emerald-500 to-green-600 text-white py-5 rounded-2xl font-black uppercase text-xs tracking-[0.1em] shadow-[0_10px_25px_rgba(16,185,129,0.4)] hover:scale-[1.02] active:scale-95 transition-all duration-300 pointer-events-auto"
>
Valider et Publier
</button>
<button
onClick={(e) => { e.preventDefault(); e.stopPropagation(); handleReject(inspectingJob.id || (inspectingJob as any)._id); }}
className="flex-1 bg-red-50 text-red-600 py-5 rounded-2xl font-black uppercase text-xs tracking-[0.1em] hover:bg-red-100 transition-all active:scale-95 pointer-events-auto"
>
Rejeter l'offre
</button>
</div>
)}
</div>
</div>
)
}
{/* MODAL : INSPECTEUR DE PROFIL UTILISATEUR (DÉTAILS COMPLETS) */}
{inspectingUser && (
<div className={`fixed inset-0 ${language === Language.AR ? 'lg:pr-80' : 'lg:pl-80'} bg-slate-900/40 backdrop-blur-sm z-[1000] flex items-center justify-center p-6 animate-in fade-in duration-300`}>
<div className="bg-white w-full max-w-2xl rounded-[3rem] shadow-[0_50px_100px_rgba(0,0,0,0.15)] overflow-hidden relative animate-in zoom-in-95 duration-500">
{/* Header de la Modal avec Identité visuelle */}
<div className="bg-[#001e3c] p-10 text-white relative">
<button onClick={() => setInspectingUser(null)} className="absolute top-8 right-8 text-white/50 hover:text-white transition-colors">
<i className="fas fa-times text-2xl"></i>
</button>
<div className="flex items-center gap-6">
{/* Avatar généré à partir des initiales */}
<div className="w-20 h-20 bg-blue-600 rounded-[2rem] flex items-center justify-center text-3xl font-black shadow-lg">
{inspectingUser.name.split(' ').map(n => n[0]).join('')}
</div>
<div>
<h2 className="text-3xl font-black tracking-tight">{inspectingUser.name}</h2>
<div className="flex items-center gap-3 mt-2">
<span className="bg-white/10 px-3 py-1 rounded-lg text-[10px] font-black uppercase tracking-widest">{inspectingUser.role}</span>
<span className={`px-3 py-1 rounded-lg text-[10px] font-black uppercase tracking-widest ${inspectingUser.isActive ? 'bg-emerald-500/20 text-emerald-300' : 'bg-rose-500/20 text-rose-300'}`}>
{inspectingUser.isActive ? 'Compte Actif' : 'Compte Suspendu'}
</span>
</div>
</div>
</div>
</div>
<div className="p-10 max-h-[60vh] overflow-y-auto">
<div className="space-y-8">
{/* Informations de base (Email, ID) */}
<div className="grid grid-cols-2 gap-8">
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Adresse Email</p>
<p className="font-bold text-slate-800">{inspectingUser.email}</p>
</div>
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">ID Membre</p>
<p className="font-mono text-xs text-slate-500">#{inspectingUser.id.substring(0, 8)}...</p>
</div>
</div>
{/* Détails spécifiques si l'utilisateur est un CANDIDAT */}
{inspectingUser.role === UserRole.CANDIDATE && (
<div className="space-y-6 pt-6 border-t border-slate-50">
<h4 className="text-[11px] font-black text-blue-600 uppercase tracking-widest">Détails Candidat</h4>
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Bio / Présentation</p>
<p className="text-sm text-slate-600 leading-relaxed italic">"{inspectingUser.candidateProfile?.bio || 'Aucune biographie renseignée.'}"</p>
</div>
<div>
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest mb-3">Compétences déclarées</p>
<div className="flex flex-wrap gap-2">
{inspectingUser.candidateProfile?.skills?.map(s => <span key={s} className="bg-slate-100 px-3 py-1.5 rounded-lg text-[10px] font-black text-slate-600">{s}</span>) || 'Aucune compétence listée'}
</div>
</div>
</div>
)}
{/* Détails spécifiques si l'utilisateur est un RECRUTEUR */}
{inspectingUser.role === UserRole.RECRUITER && (
<div className="space-y-6 pt-6 border-t border-slate-50">
<h4 className="text-[11px] font-black text-blue-600 uppercase tracking-widest">Informations Entreprise</h4>
<div className="grid grid-cols-2 gap-6">
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Société</p>
<p className="font-black text-slate-800">{inspectingUser.recruiterProfile?.companyName || inspectingUser.company || 'N/A'}</p>
</div>
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Matricule Fiscal</p>
<p className="font-bold text-slate-800">{inspectingUser.recruiterProfile?.taxId || 'Non renseigné'}</p>
</div>
</div>
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Site Web Officiel</p>
<a href={inspectingUser.recruiterProfile?.website} target="_blank" className="text-sm text-blue-600 font-bold hover:underline">{inspectingUser.recruiterProfile?.website || 'N/A'}</a>
</div>
</div>
)}
{/* Détails spécifiques si l'utilisateur est STAFF (RH ou ADMIN) */}
{(inspectingUser.role === UserRole.RH || inspectingUser.role === UserRole.ADMIN) && (
<div className="space-y-6 pt-6 border-t border-slate-50">
<h4 className="text-[11px] font-black text-blue-600 uppercase tracking-widest">Espace Personnel Interne</h4>
<div className="space-y-1">
<p className="text-[10px] font-black text-slate-400 uppercase tracking-widest">Département de gestion</p>
<p className="font-black text-slate-800">{inspectingUser.rhProfile?.department || inspectingUser.adminProfile?.department || 'Administration Centrale MatchJob'}</p>
</div>
</div>
)}
</div>
</div>
{/* BARRE D'ACTIONS RAPIDES POUR L'ADMINISTRATEUR */}
<div className="p-10 bg-slate-50 border-t border-slate-100">
<div className="flex gap-4">
{/* Bouton de bannissement / réactivation temporaire */}
<button
onClick={() => onToggleStatus(inspectingUser.id)}
className={`flex-1 py-4 rounded-2xl font-black uppercase text-xs tracking-widest transition-all ${inspectingUser.isActive ? 'bg-amber-50 text-amber-600 hover:bg-amber-100' : 'bg-emerald-50 text-emerald-600 hover:bg-emerald-100'}`}
>
<i className={`fas ${inspectingUser.isActive ? 'fa-user-slash' : 'fa-user-check'} mr-3`}></i>
{inspectingUser.isActive ? "Suspendre l'accès" : "Réactiver l'accès"}
</button>
{/* Bouton de suppression irréversible */}
<button
onClick={() => { if (window.confirm('Supprimer définitivement cet utilisateur ?')) onDeleteUser(inspectingUser.id); setInspectingUser(null); }}
className="flex-1 bg-rose-50 text-rose-600 py-4 rounded-2xl font-black uppercase text-xs tracking-widest hover:bg-rose-100 transition-all"
>
<i className="fas fa-trash-alt mr-3"></i> Supprimer le compte
</button>
</div>
</div>
</div>
</div>
)}
</main>
</div>
);
};
export default AdminDashboard;