'use client' import * as React from 'react' import Link from 'next/link' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Progress } from '@/components/ui/progress' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { ScrollArea } from '@/components/ui/scroll-area' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, LineChart, Line, Legend, AreaChart, Area, } from 'recharts' import { FileText, Clock, CheckCircle2, AlertTriangle, TrendingUp, TrendingDown, Users, ArrowRight, Calendar, Target, Activity, Timer, AlertCircle, } from 'lucide-react' import { cn } from '@/lib/utils' import { requisitions, countries, recruiterCapacity, getDashboardStats } from '@/lib/mock-data' import { STATUS_CONFIG, type Requisition } from '@/lib/types' // Chart data const statusDistribution = [ { name: 'Reclutando', value: 4, color: '#6ec1e4' }, { name: 'Pendiente RR.HH.', value: 1, color: '#f59e0b' }, { name: 'Pendiente Admin', value: 1, color: '#8b5cf6' }, { name: 'Corrección', value: 1, color: '#f97316' }, { name: 'Cerrado', value: 1, color: '#22c55e' }, { name: 'Cancelado', value: 1, color: '#ef4444' }, ] const requisitionsByCountry = [ { country: 'México', abiertas: 3, cerradas: 0, canceladas: 0 }, { country: 'Colombia', abiertas: 2, cerradas: 0, canceladas: 0 }, { country: 'Perú', abiertas: 1, cerradas: 0, canceladas: 0 }, { country: 'Chile', abiertas: 0, cerradas: 1, canceladas: 0 }, { country: 'Rep. Dom.', abiertas: 1, cerradas: 0, canceladas: 0 }, { country: 'Argentina', abiertas: 1, cerradas: 0, canceladas: 0 }, { country: 'Guatemala', abiertas: 0, cerradas: 0, canceladas: 1 }, ] const ttfTrend = [ { mes: 'Sep', dias: 28 }, { mes: 'Oct', dias: 25 }, { mes: 'Nov', dias: 22 }, { mes: 'Dic', dias: 20 }, { mes: 'Ene', dias: 18 }, ] const stageVelocity = [ { stage: 'Búsqueda', promedio: 5 }, { stage: '1era Entrev.', promedio: 3 }, { stage: '2da Entrev.', promedio: 4 }, { stage: 'Referencias', promedio: 3 }, { stage: 'Exámenes', promedio: 2 }, { stage: 'Selección', promedio: 1 }, ] function getInitials(name: string): string { return name .split(' ') .map(n => n[0]) .join('') .toUpperCase() .slice(0, 2) } function getDaysUntilDeadline(deadline: string): number { const now = new Date() const deadlineDate = new Date(deadline) return Math.ceil((deadlineDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)) } function getCapacityStatus(score: number, max: number): { label: string; color: string; bgColor: string } { const percentage = (score / max) * 100 if (percentage < 60) return { label: 'Disponible', color: 'text-emerald-700 dark:text-emerald-400', bgColor: 'bg-emerald-100 dark:bg-emerald-900/30' } if (percentage < 90) return { label: 'A capacidad', color: 'text-amber-700 dark:text-amber-400', bgColor: 'bg-amber-100 dark:bg-amber-900/30' } return { label: 'Sobrecargado', color: 'text-red-700 dark:text-red-400', bgColor: 'bg-red-100 dark:bg-red-900/30' } } interface StatCardProps { title: string value: string | number subtitle?: string icon: React.ElementType trend?: { value: number; positive: boolean } iconColor?: string iconBgColor?: string } function StatCard({ title, value, subtitle, icon: Icon, trend, iconColor = 'text-primary', iconBgColor = 'bg-primary/10' }: StatCardProps) { return (

{title}

{value}

{trend && ( {trend.positive ? : } {trend.value}% )}
{subtitle &&

{subtitle}

}
) } function RequisitionRow({ requisition }: { requisition: Requisition }) { const status = STATUS_CONFIG[requisition.status] const daysLeft = getDaysUntilDeadline(requisition.hardDeadline) const isOverdue = daysLeft < 0 const isAtRisk = daysLeft >= 0 && daysLeft <= 7 return (
{requisition.id} {status.label}

{requisition.positionName} - {requisition.cityName}, {requisition.countryName}

{requisition.vacanciesFilled}/{requisition.quantity} {isOverdue ? `${Math.abs(daysLeft)}d vencido` : `${daysLeft}d restantes`}
) } export function DashboardPage() { const [selectedCountry, setSelectedCountry] = React.useState('all') const stats = getDashboardStats() const filteredRequisitions = selectedCountry === 'all' ? requisitions : requisitions.filter(r => r.countryId === selectedCountry) const openRequisitions = filteredRequisitions.filter(r => !['cerrado', 'cancelado'].includes(r.status)) const urgentRequisitions = openRequisitions.filter(r => { const daysLeft = getDaysUntilDeadline(r.hardDeadline) return daysLeft <= 7 }).sort((a, b) => new Date(a.hardDeadline).getTime() - new Date(b.hardDeadline).getTime()) return (
{/* Header */}

Dashboard

Resumen ejecutivo de requisiciones y reclutamiento

{/* Stats Grid */}
{/* Charts Row */}
{/* Status Distribution */} Distribución por Estado Estado actual de todas las requisiciones
{statusDistribution.map((entry, index) => ( ))} [value, 'Requisiciones']} contentStyle={{ backgroundColor: 'hsl(var(--card))', border: '1px solid hsl(var(--border))', borderRadius: '8px', }} /> {value}} />
{/* Requisitions by Country */} Requisiciones por País Distribución geográfica de vacantes
{/* TTF Trend & Stage Velocity */}
{/* TTF Trend */} Tendencia TTF Tiempo promedio para cubrir vacantes (días)
[`${value} días`, 'TTF Promedio']} contentStyle={{ backgroundColor: 'hsl(var(--card))', border: '1px solid hsl(var(--border))', borderRadius: '8px', }} />
{/* Stage Velocity */} Velocidad por Etapa Días promedio en cada fase del proceso
[`${value} días`, 'Promedio']} contentStyle={{ backgroundColor: 'hsl(var(--card))', border: '1px solid hsl(var(--border))', borderRadius: '8px', }} />
{/* Bottom Row */}
{/* Urgent Cases */}
Casos Urgentes Requisiciones con SLA en riesgo o vencido
{urgentRequisitions.length > 0 ? (
{urgentRequisitions.map((req) => ( ))}
) : (

Sin casos urgentes

Todas las requisiciones están dentro del SLA

)}
{/* Recruiter Capacity */}
Carga de Reclutadores Capacidad actual del equipo
{recruiterCapacity.map((recruiter) => { const capacityStatus = getCapacityStatus(recruiter.currentScore, recruiter.maxThreshold) const percentage = Math.min((recruiter.currentScore / recruiter.maxThreshold) * 100, 100) return (
{getInitials(recruiter.recruiterName)}

{recruiter.recruiterName}

{recruiter.countryFocus.join(', ')}

{capacityStatus.label}
Score: {recruiter.currentScore}/{recruiter.maxThreshold} {recruiter.openCases} casos
= 90 && '[&>div]:bg-red-500', percentage >= 60 && percentage < 90 && '[&>div]:bg-amber-500', percentage < 60 && '[&>div]:bg-emerald-500' )} />
) })}
) }