'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 { Input } from '@/components/ui/input' import { Textarea } from '@/components/ui/textarea' import { Label } from '@/components/ui/label' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { ScrollArea } from '@/components/ui/scroll-area' import { Separator } from '@/components/ui/separator' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Search, Clock, CheckCircle2, XCircle, AlertTriangle, Eye, MapPin, Building2, Users, Calendar, DollarSign, FileText, ThumbsUp, ThumbsDown, ArrowRight, Filter, } from 'lucide-react' import { cn } from '@/lib/utils' import { requisitions, countries, currentUser } from '@/lib/mock-data' import { STATUS_CONFIG, URGENCY_CONFIG, type Requisition } from '@/lib/types' import { toast } from 'sonner' 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 formatDate(dateString: string): string { return new Date(dateString).toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric', }) } function getDaysSinceCreated(dateString: string): number { const now = new Date() const created = new Date(dateString) return Math.ceil((now.getTime() - created.getTime()) / (1000 * 60 * 60 * 24)) } interface ApprovalCardProps { requisition: Requisition approvalType: 'rrhh' | 'admin' onApprove: (req: Requisition) => void onReject: (req: Requisition) => void onView: (req: Requisition) => void } function ApprovalCard({ requisition, approvalType, onApprove, onReject, onView }: ApprovalCardProps) { const urgency = URGENCY_CONFIG[requisition.urgency] const daysLeft = getDaysUntilDeadline(requisition.hardDeadline) const daysPending = getDaysSinceCreated(requisition.createdAt) const isOverdue = daysLeft < 0 const isAtRisk = daysLeft >= 0 && daysLeft <= 7 const isPendingTooLong = daysPending > 2 return (
{/* Header */}
{requisition.id} {urgency.label} {isPendingTooLong && ( {daysPending}d pendiente )}
{/* Position & Location */}

{requisition.positionName}

{requisition.cityName}, {requisition.countryName} {requisition.departmentName} {requisition.quantity} vacante{requisition.quantity > 1 ? 's' : ''}
{/* Justification Preview */}

{requisition.justification}

{/* Requester & SLA */}
{getInitials(requisition.requesterName)} {requisition.requesterName} - {formatDate(requisition.createdAt)}
SLA: {isOverdue ? `${Math.abs(daysLeft)}d vencido` : `${daysLeft}d`}
{/* HR Approval Info (for Admin view) */} {approvalType === 'admin' && requisition.approvedByHrAt && (
Aprobado por RR.HH. el {formatDate(requisition.approvedByHrAt)}
)} {/* Compensation (visible to authorized roles) */} {(requisition.salaryPackage || requisition.budget) && (
{requisition.salaryPackage && ( {requisition.salaryPackage} )} {requisition.budget && ( Presupuesto: {requisition.currency} {requisition.budget.toLocaleString()} )}
)}
{/* Action Buttons */}
) } export function ApprovalsPage() { const [searchTerm, setSearchTerm] = React.useState('') const [countryFilter, setCountryFilter] = React.useState('all') const [activeTab, setActiveTab] = React.useState<'rrhh' | 'admin'>('rrhh') // Dialog states const [selectedRequisition, setSelectedRequisition] = React.useState(null) const [approveDialogOpen, setApproveDialogOpen] = React.useState(false) const [rejectDialogOpen, setRejectDialogOpen] = React.useState(false) const [detailDialogOpen, setDetailDialogOpen] = React.useState(false) const [rejectComment, setRejectComment] = React.useState('') const [approveComment, setApproveComment] = React.useState('') // Filter requisitions based on approval type const pendingHR = requisitions.filter(r => r.status === 'pendiente_rrhh') const pendingAdmin = requisitions.filter(r => r.status === 'pendiente_admin') const currentQueue = activeTab === 'rrhh' ? pendingHR : pendingAdmin const filteredQueue = React.useMemo(() => { let filtered = [...currentQueue] if (searchTerm) { const term = searchTerm.toLowerCase() filtered = filtered.filter(r => r.id.toLowerCase().includes(term) || r.positionName.toLowerCase().includes(term) || r.requesterName.toLowerCase().includes(term) ) } if (countryFilter !== 'all') { filtered = filtered.filter(r => r.countryId === countryFilter) } // Sort by urgency and then by date filtered.sort((a, b) => { const urgencyOrder = { critica: 0, alta: 1, normal: 2 } const urgencyDiff = urgencyOrder[a.urgency] - urgencyOrder[b.urgency] if (urgencyDiff !== 0) return urgencyDiff return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() }) return filtered }, [currentQueue, searchTerm, countryFilter]) const handleApprove = (requisition: Requisition) => { setSelectedRequisition(requisition) setApproveDialogOpen(true) } const handleReject = (requisition: Requisition) => { setSelectedRequisition(requisition) setRejectDialogOpen(true) } const handleView = (requisition: Requisition) => { setSelectedRequisition(requisition) setDetailDialogOpen(true) } const confirmApprove = () => { if (!selectedRequisition) return toast.success('Requisición aprobada', { description: `${selectedRequisition.id} ha sido aprobada y pasará al siguiente paso.`, }) setApproveDialogOpen(false) setApproveComment('') setSelectedRequisition(null) } const confirmReject = () => { if (!selectedRequisition || !rejectComment.trim()) return toast.success('Requisición devuelta', { description: `${selectedRequisition.id} ha sido devuelta al solicitante para corrección.`, }) setRejectDialogOpen(false) setRejectComment('') setSelectedRequisition(null) } return (
{/* Header */}

Aprobaciones

Gestiona las requisiciones pendientes de aprobación

{/* Stats Cards */}

Pendientes RR.HH.

{pendingHR.length}

Pendientes Admin

{pendingAdmin.length}

Urgentes

{[...pendingHR, ...pendingAdmin].filter(r => r.urgency === 'critica').length}

{'>'}48h Pendientes

{[...pendingHR, ...pendingAdmin].filter(r => getDaysSinceCreated(r.createdAt) > 2).length}

{/* Tabs */} setActiveTab(v as 'rrhh' | 'admin')}>
RR.HH. ({pendingHR.length}) Administración ({pendingAdmin.length}) {/* Filters */}
setSearchTerm(e.target.value)} className="pl-9" />
{filteredQueue.length > 0 ? (
{filteredQueue.map((requisition) => ( ))}
) : (

Sin pendientes

No hay requisiciones pendientes de aprobación de RR.HH.

)}
{filteredQueue.length > 0 ? (
{filteredQueue.map((requisition) => ( ))}
) : (

Sin pendientes

No hay requisiciones pendientes de aprobación de Administración

)}
{/* Approve Dialog */} Aprobar Requisición {selectedRequisition && ( <> Estás por aprobar {selectedRequisition.id} - {selectedRequisition.positionName} )}