import { useState, useEffect, useCallback } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; import { Button } from '../ui/button'; import { Badge } from '../ui/badge'; import { Textarea } from '../ui/textarea'; import { Input } from '../ui/input'; import { Label } from '../ui/label'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog'; import { Alert, AlertDescription } from '../ui/alert'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs'; import { CheckCircle2, XCircle, Calendar, Award, MapPin, Loader2, RefreshCw, Clock, Video, } from 'lucide-react'; import adminApi, { type PendingSheikhDto } from '../../lib/api/adminApi'; import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; import { useLanguage } from '../lib/languageContext'; import { translations } from '../../lib/translations'; // ─── Types ──────────────────────────────────────────────────────────────────── interface ScheduledInterview { sheikhId: number; name: string; country: string; specializations: string; experienceYears: number; scheduledAt: Date | null; } // ─── Helpers ────────────────────────────────────────────────────────────────── function formatCountdown(ms: number): string { if (ms <= 0) return 'Now'; const totalSecs = Math.floor(ms / 1000); const days = Math.floor(totalSecs / 86400); const hours = Math.floor((totalSecs % 86400) / 3600); const mins = Math.floor((totalSecs % 3600) / 60); const secs = totalSecs % 60; if (days > 0) return `${days}d ${hours}h ${mins}m`; if (hours > 0) return `${hours}h ${mins}m ${secs}s`; return `${mins}m ${secs}s`; } function isWithin5Mins(scheduledAt: Date | null, now: Date): boolean { if (!scheduledAt) return false; const diff = scheduledAt.getTime() - now.getTime(); return diff <= 5 * 60 * 1000 && diff > -30 * 60 * 1000; } // ─── Countdown badge ────────────────────────────────────────────────────────── function CountdownBadge({ scheduledAt, now }: { scheduledAt: Date | null; now: Date }) { if (!scheduledAt) { return ( No time set ); } const diff = scheduledAt.getTime() - now.getTime(); const imminent = diff <= 5 * 60 * 1000 && diff > 0; const past = diff <= 0; if (past) { return ( In progress ); } if (imminent) { return ( {formatCountdown(diff)} ); } return ( {formatCountdown(diff)} ); } // ─── Under Review Card ──────────────────────────────────────────────────────── function UnderReviewCard({ interview, now, onApprove, onReject, onJoin, processing, isFocus = false, t, }: { interview: ScheduledInterview; now: Date; onApprove: () => void; onReject: () => void; onJoin: () => void; processing: boolean; isFocus?: boolean; t: ReturnType[string]['sheikhApproval']; }) { const diff = interview.scheduledAt ? interview.scheduledAt.getTime() - now.getTime() : null; const isLive = diff !== null && diff <= 0 && diff > -30 * 60 * 1000; return ( {isFocus && (
{isLive ? t.interviewLiveNow : t.nextInterviewSoon}
)}
{interview.name.split(' ').map(n => n[0]).join('').slice(0, 2).toUpperCase()}
{interview.name}
{interview.country} · {interview.experienceYears} {t.yearsExperience}
{/* Prominent countdown for focused card */} {isFocus && (
{isLive ? (
LIVE NOW
) : ( <>
Starts in
{diff !== null ? formatCountdown(diff) : '—'}
)}
)}

{interview.specializations}

{/* Interview time row */} {interview.scheduledAt ? (
{interview.scheduledAt.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })} {' at '} {interview.scheduledAt.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}
) : (
{t.noInterviewTime}
)} {/* Action buttons — stack on mobile when 3 shown */}
{isLive && ( )}
); } // ─── Main Component ─────────────────────────────────────────────────────────── export function SheikhApproval() { const navigate = useNavigate(); const { language, isRTL } = useLanguage(); const t = translations[language].sheikhApproval; const [pendingSheikhs, setPendingSheikhs] = useState([]); const [scheduledInterviews, setScheduledInterviews] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [now, setNow] = useState(new Date()); const [focusOverride, setFocusOverride] = useState(false); const [activeTab, setActiveTab] = useState<'pending' | 'under-review'>('pending'); const [selectedSheikh, setSelectedSheikh] = useState(null); const [showDialog, setShowDialog] = useState(false); const [actionType, setActionType] = useState<'approve' | 'reject' | 'interview'>('approve'); const [rejectionReason, setRejectionReason] = useState(''); const [interviewDate, setInterviewDate] = useState(''); const [interviewTime, setInterviewTime] = useState(''); const [processing, setProcessing] = useState(false); // ── Tick every second ────────────────────────────────────────────────────── useEffect(() => { const timer = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(timer); }, []); // ── Focus mode logic & auto-switch ───────────────────────────────────────── const imminentInterview = scheduledInterviews.find(i => isWithin5Mins(i.scheduledAt, now)); const showFocusMode = !!imminentInterview && !focusOverride; const [lastImminentId, setLastImminentId] = useState(null); useEffect(() => { if (imminentInterview) { if (imminentInterview.sheikhId !== lastImminentId) { setLastImminentId(imminentInterview.sheikhId); if (!focusOverride) setActiveTab('under-review'); } } else { setLastImminentId(null); setFocusOverride(false); } }, [imminentInterview, lastImminentId, focusOverride]); // ── Load data ────────────────────────────────────────────────────────────── const loadData = useCallback(async () => { setLoading(true); setError(null); try { const [pending, underReview] = await Promise.all([ adminApi.fetchPendingSheikhs(), adminApi.fetchSheikhsByStatus('UNDER_REVIEW'), ]); setPendingSheikhs(pending); const interviews: ScheduledInterview[] = underReview .map(s => ({ sheikhId: s.id, name: s.name, country: s.country, specializations: s.specializations, experienceYears: s.experienceYears, scheduledAt: s.interviewDateTime ? new Date(s.interviewDateTime) : null, })) .sort((a, b) => { if (!a.scheduledAt && !b.scheduledAt) return 0; if (!a.scheduledAt) return 1; if (!b.scheduledAt) return -1; return a.scheduledAt.getTime() - b.scheduledAt.getTime(); }); setScheduledInterviews(interviews); } catch (err) { const message = err instanceof Error ? err.message : 'Unknown error'; setError(`${t.failedToLoad}: ${message}`); } finally { setLoading(false); } }, [t]); useEffect(() => { loadData(); }, [loadData]); // ── Actions ──────────────────────────────────────────────────────────────── const handleAction = (sheikhId: number, action: 'approve' | 'reject' | 'interview') => { setSelectedSheikh(sheikhId); setActionType(action); setShowDialog(true); }; const confirmAction = async () => { if (selectedSheikh === null) return; setProcessing(true); try { if (actionType === 'approve') { await adminApi.approveSheikh(selectedSheikh); } else if (actionType === 'reject') { await adminApi.rejectSheikh(selectedSheikh, rejectionReason); } else { await adminApi.scheduleInterview(selectedSheikh, interviewDate, interviewTime); } setShowDialog(false); setRejectionReason(''); setInterviewDate(''); setInterviewTime(''); await loadData(); } catch (err) { console.error(`Failed to ${actionType} sheikh:`, err); toast.error(`${t.failedToAction} ${actionType}. ${t.pleaseTryAgain}`); } finally { setProcessing(false); } }; // ── Pending card ─────────────────────────────────────────────────────────── const renderPendingCard = (sheikh: PendingSheikhDto) => (
{sheikh.name.split(' ').map(n => n[0]).join('').slice(0, 2).toUpperCase()}
{sheikh.name}
{sheikh.County} · {sheikh.experienceYears} {t.yearsExperience}
{t.pending}

{sheikh.specialization}

{/* Stacked buttons on mobile, row on sm+ */}
); // ── Dialog title/description helpers ─────────────────────────────────────── const dialogTitle = actionType === 'approve' ? t.approveApplication : actionType === 'reject' ? t.rejectApplication : t.scheduleInterview; const dialogDescription = actionType === 'approve' ? t.approveDescription : actionType === 'reject' ? t.rejectDescription : t.scheduleDescription; // ── Render ───────────────────────────────────────────────────────────────── return (
{/* Page header */}

{t.sheikhApprovalManagement}

{t.reviewManageApplications}

{/* */}
{error && ( {error} )} {loading ? (
) : ( setActiveTab(val as 'pending' | 'under-review')}> {t.pending} {pendingSheikhs.length > 0 && ( {pendingSheikhs.length} )} {t.interviewScheduled} {scheduledInterviews.length > 0 && ( {scheduledInterviews.length} )} {/* ── Pending tab ── */} {pendingSheikhs.length > 0 ? (
{pendingSheikhs.map(renderPendingCard)}
) : ( {t.noPending} )}
{/* ── Under Review tab ── */} {scheduledInterviews.length > 0 ? (
{showFocusMode && imminentInterview ? (
handleAction(imminentInterview.sheikhId, 'approve')} onReject={() => handleAction(imminentInterview.sheikhId, 'reject')} onJoin={() => navigate(`/admin/interview/${imminentInterview.sheikhId}`)} isFocus t={t} />
) : (
{scheduledInterviews.map(interview => ( handleAction(interview.sheikhId, 'approve')} onReject={() => handleAction(interview.sheikhId, 'reject')} onJoin={() => navigate(`/admin/interview/${interview.sheikhId}`)} isFocus={interview.sheikhId === imminentInterview?.sheikhId} t={t} /> ))}
{imminentInterview && (
)}
)}
) : ( {t.noInterviewsScheduled} )}
)} {/* ── Action Dialog ── */} {dialogTitle} {dialogDescription}
{actionType === 'reject' && (