Initial commit: Proyecto portal-requisicion-vacantes

This commit is contained in:
Isaac_Aracena
2026-06-12 16:08:49 -04:00
commit b5a16d8daf
112 changed files with 18334 additions and 0 deletions
+720
View File
@@ -0,0 +1,720 @@
// Mock Data for GLM Portal - This simulates backend data
import type {
User,
Country,
City,
Department,
Position,
Requisition,
RecruiterCapacity,
Notification,
AuditLog
} from './types'
// Current logged-in user (simulated)
export const currentUser: User = {
id: 'usr_001',
email: 'maria.gonzalez@gomezleemarketing.com',
name: 'María González',
role: 'director_rrhh',
countryId: 'all',
avatar: undefined,
active: true
}
// Countries
export const countries: Country[] = [
{ id: 'mx', name: 'México', code: 'MX', timezone: 'America/Mexico_City', active: true },
{ id: 'co', name: 'Colombia', code: 'CO', timezone: 'America/Bogota', active: true },
{ id: 'pe', name: 'Perú', code: 'PE', timezone: 'America/Lima', active: true },
{ id: 'cl', name: 'Chile', code: 'CL', timezone: 'America/Santiago', active: true },
{ id: 'ar', name: 'Argentina', code: 'AR', timezone: 'America/Buenos_Aires', active: true },
{ id: 'do', name: 'República Dominicana', code: 'DO', timezone: 'America/Santo_Domingo', active: true },
{ id: 'gt', name: 'Guatemala', code: 'GT', timezone: 'America/Guatemala', active: true },
{ id: 'pa', name: 'Panamá', code: 'PA', timezone: 'America/Panama', active: true },
]
// Cities
export const cities: City[] = [
// México
{ id: 'mx_cdmx', countryId: 'mx', name: 'Ciudad de México' },
{ id: 'mx_gdl', countryId: 'mx', name: 'Guadalajara' },
{ id: 'mx_mty', countryId: 'mx', name: 'Monterrey' },
{ id: 'mx_pue', countryId: 'mx', name: 'Puebla' },
// Colombia
{ id: 'co_bog', countryId: 'co', name: 'Bogotá' },
{ id: 'co_med', countryId: 'co', name: 'Medellín' },
{ id: 'co_cal', countryId: 'co', name: 'Cali' },
{ id: 'co_bar', countryId: 'co', name: 'Barranquilla' },
// Perú
{ id: 'pe_lim', countryId: 'pe', name: 'Lima' },
{ id: 'pe_are', countryId: 'pe', name: 'Arequipa' },
{ id: 'pe_tru', countryId: 'pe', name: 'Trujillo' },
// Chile
{ id: 'cl_stg', countryId: 'cl', name: 'Santiago' },
{ id: 'cl_val', countryId: 'cl', name: 'Valparaíso' },
{ id: 'cl_con', countryId: 'cl', name: 'Concepción' },
// Argentina
{ id: 'ar_bue', countryId: 'ar', name: 'Buenos Aires' },
{ id: 'ar_cor', countryId: 'ar', name: 'Córdoba' },
{ id: 'ar_ros', countryId: 'ar', name: 'Rosario' },
// República Dominicana
{ id: 'do_sdo', countryId: 'do', name: 'Santo Domingo' },
{ id: 'do_stg', countryId: 'do', name: 'Santiago de los Caballeros' },
// Guatemala
{ id: 'gt_gua', countryId: 'gt', name: 'Ciudad de Guatemala' },
// Panamá
{ id: 'pa_pan', countryId: 'pa', name: 'Ciudad de Panamá' },
]
// Departments
export const departments: Department[] = [
{ id: 'dept_sales', name: 'Ventas', countryId: 'all' },
{ id: 'dept_mkt', name: 'Marketing', countryId: 'all' },
{ id: 'dept_ops', name: 'Operaciones', countryId: 'all' },
{ id: 'dept_trade', name: 'Trade Marketing', countryId: 'all' },
{ id: 'dept_fin', name: 'Finanzas', countryId: 'all' },
{ id: 'dept_hr', name: 'Recursos Humanos', countryId: 'all' },
{ id: 'dept_it', name: 'Tecnología', countryId: 'all' },
{ id: 'dept_admin', name: 'Administración', countryId: 'all' },
]
// Positions
export const positions: Position[] = [
{ id: 'pos_merch', name: 'Merchandiser', complexityTier: 1 },
{ id: 'pos_prom', name: 'Promotor/a', complexityTier: 1 },
{ id: 'pos_imp', name: 'Impulsador/a', complexityTier: 1 },
{ id: 'pos_dem', name: 'Demostrador/a', complexityTier: 1 },
{ id: 'pos_sales_jr', name: 'Ejecutivo de Ventas Jr', complexityTier: 2 },
{ id: 'pos_sales_sr', name: 'Ejecutivo de Ventas Sr', complexityTier: 2 },
{ id: 'pos_analyst', name: 'Analista', complexityTier: 2 },
{ id: 'pos_trade', name: 'Trade Marketing Specialist', complexityTier: 2 },
{ id: 'pos_coord', name: 'Coordinador', complexityTier: 3 },
{ id: 'pos_super', name: 'Supervisor', complexityTier: 3 },
{ id: 'pos_manager', name: 'Gerente', complexityTier: 3 },
{ id: 'pos_director', name: 'Director', complexityTier: 3 },
]
// Users
export const users: User[] = [
currentUser,
{ id: 'usr_002', email: 'carlos.mendez@gomezleemarketing.com', name: 'Carlos Méndez', role: 'reclutador', countryId: 'mx', active: true },
{ id: 'usr_003', email: 'ana.rodriguez@gomezleemarketing.com', name: 'Ana Rodríguez', role: 'reclutador', countryId: 'co', active: true },
{ id: 'usr_004', email: 'luis.torres@gomezleemarketing.com', name: 'Luis Torres', role: 'reclutador', countryId: 'pe', active: true },
{ id: 'usr_005', email: 'sofia.vargas@gomezleemarketing.com', name: 'Sofía Vargas', role: 'rrhh_regional', countryId: 'mx', active: true },
{ id: 'usr_006', email: 'pedro.silva@gomezleemarketing.com', name: 'Pedro Silva', role: 'administracion', countryId: 'all', active: true },
{ id: 'usr_007', email: 'elena.castro@gomezleemarketing.com', name: 'Elena Castro', role: 'solicitante', countryId: 'mx', active: true },
{ id: 'usr_008', email: 'roberto.diaz@gomezleemarketing.com', name: 'Roberto Díaz', role: 'country_lead', countryId: 'co', active: true },
{ id: 'usr_009', email: 'laura.martinez@gomezleemarketing.com', name: 'Laura Martínez', role: 'reclutador', countryId: 'cl', active: true },
{ id: 'usr_010', email: 'diego.hernandez@gomezleemarketing.com', name: 'Diego Hernández', role: 'solicitante', countryId: 'co', active: true },
{ id: 'usr_011', email: 'miguel.admin@gomezleemarketing.com', name: 'Miguel Admin', role: 'admin_sistema', countryId: 'all', active: true },
{ id: 'usr_012', email: 'carmen.inactive@gomezleemarketing.com', name: 'Carmen Inactiva', role: 'reclutador', countryId: 'mx', active: false },
{ id: 'usr_013', email: 'jorge.inactive@gomezleemarketing.com', name: 'Jorge Inactivo', role: 'solicitante', countryId: 'co', active: false },
]
// Mock Requisitions
export const requisitions: Requisition[] = [
{
id: 'REQ-2024-001',
requesterId: 'usr_007',
requesterName: 'Elena Castro',
countryId: 'mx',
countryName: 'México',
cityId: 'mx_cdmx',
cityName: 'Ciudad de México',
departmentId: 'dept_trade',
departmentName: 'Trade Marketing',
positionId: 'pos_merch',
positionName: 'Merchandiser',
quantity: 5,
vacanciesFilled: 3,
modalidad: 'presencial',
tipoContratacion: 'nuevo',
justification: 'Expansión de cobertura en autoservicios zona norte CDMX para campaña Q1 2024',
urgency: 'alta',
status: 'reclutando',
recruiterId: 'usr_002',
recruiterName: 'Carlos Méndez',
createdAt: '2024-01-15T10:30:00Z',
hardDeadline: '2024-02-15T23:59:59Z',
approvedByHrAt: '2024-01-16T14:22:00Z',
approvedByHrUserId: 'usr_005',
approvedByAdminAt: '2024-01-17T09:15:00Z',
approvedByAdminUserId: 'usr_006',
currentStage: 'segunda_entrevista',
},
{
id: 'REQ-2024-002',
requesterId: 'usr_010',
requesterName: 'Diego Hernández',
countryId: 'co',
countryName: 'Colombia',
cityId: 'co_bog',
cityName: 'Bogotá',
departmentId: 'dept_sales',
departmentName: 'Ventas',
positionId: 'pos_sales_sr',
positionName: 'Ejecutivo de Ventas Sr',
quantity: 2,
vacanciesFilled: 0,
modalidad: 'hibrido',
tipoContratacion: 'reemplazo',
justification: 'Reemplazo por renuncia de personal. Cuentas clave en riesgo.',
urgency: 'critica',
status: 'pendiente_admin',
createdAt: '2024-01-20T08:45:00Z',
hardDeadline: '2024-02-20T23:59:59Z',
approvedByHrAt: '2024-01-20T16:30:00Z',
approvedByHrUserId: 'usr_001',
},
{
id: 'REQ-2024-003',
requesterId: 'usr_007',
requesterName: 'Elena Castro',
countryId: 'mx',
countryName: 'México',
cityId: 'mx_gdl',
cityName: 'Guadalajara',
departmentId: 'dept_ops',
departmentName: 'Operaciones',
positionId: 'pos_coord',
positionName: 'Coordinador',
quantity: 1,
vacanciesFilled: 0,
modalidad: 'presencial',
tipoContratacion: 'nuevo',
justification: 'Nueva estructura operativa región occidente para soportar crecimiento 2024',
urgency: 'normal',
salaryPackage: '$45,000 - $55,000 MXN mensual',
budget: 55000,
currency: 'MXN',
status: 'pendiente_rrhh',
createdAt: '2024-01-22T11:20:00Z',
hardDeadline: '2024-03-01T23:59:59Z',
},
{
id: 'REQ-2024-004',
requesterId: 'usr_008',
requesterName: 'Roberto Díaz',
countryId: 'co',
countryName: 'Colombia',
cityId: 'co_med',
cityName: 'Medellín',
departmentId: 'dept_trade',
departmentName: 'Trade Marketing',
positionId: 'pos_prom',
positionName: 'Promotor/a',
quantity: 10,
vacanciesFilled: 7,
modalidad: 'presencial',
tipoContratacion: 'temporal',
justification: 'Campaña especial Semana Santa 2024 en centros comerciales principales',
urgency: 'alta',
status: 'reclutando',
recruiterId: 'usr_003',
recruiterName: 'Ana Rodríguez',
createdAt: '2024-01-10T09:00:00Z',
hardDeadline: '2024-02-28T23:59:59Z',
approvedByHrAt: '2024-01-10T15:45:00Z',
approvedByHrUserId: 'usr_001',
approvedByAdminAt: '2024-01-11T10:30:00Z',
approvedByAdminUserId: 'usr_006',
currentStage: 'examenes_medicos',
},
{
id: 'REQ-2024-005',
requesterId: 'usr_010',
requesterName: 'Diego Hernández',
countryId: 'pe',
countryName: 'Perú',
cityId: 'pe_lim',
cityName: 'Lima',
departmentId: 'dept_mkt',
departmentName: 'Marketing',
positionId: 'pos_analyst',
positionName: 'Analista',
quantity: 1,
vacanciesFilled: 0,
modalidad: 'remoto',
tipoContratacion: 'nuevo',
justification: 'Soporte analítico para equipo regional de marketing digital',
urgency: 'normal',
status: 'correccion_requerida',
createdAt: '2024-01-18T14:30:00Z',
hardDeadline: '2024-03-15T23:59:59Z',
rejectionComment: 'Favor especificar el presupuesto aprobado y nivel de experiencia requerido.',
},
{
id: 'REQ-2024-006',
requesterId: 'usr_007',
requesterName: 'Elena Castro',
countryId: 'cl',
countryName: 'Chile',
cityId: 'cl_stg',
cityName: 'Santiago',
departmentId: 'dept_sales',
departmentName: 'Ventas',
positionId: 'pos_manager',
positionName: 'Gerente',
quantity: 1,
vacanciesFilled: 1,
modalidad: 'hibrido',
tipoContratacion: 'nuevo',
justification: 'Creación de gerencia comercial para mercado chileno',
urgency: 'critica',
status: 'cerrado',
recruiterId: 'usr_009',
recruiterName: 'Laura Martínez',
createdAt: '2023-12-01T08:00:00Z',
hardDeadline: '2024-01-31T23:59:59Z',
approvedByHrAt: '2023-12-01T16:00:00Z',
approvedByHrUserId: 'usr_001',
approvedByAdminAt: '2023-12-02T11:00:00Z',
approvedByAdminUserId: 'usr_006',
closedAt: '2024-01-25T10:00:00Z',
closedReason: 'Vacante cubierta exitosamente',
currentStage: 'cerrado',
},
{
id: 'REQ-2024-007',
requesterId: 'usr_008',
requesterName: 'Roberto Díaz',
countryId: 'do',
countryName: 'República Dominicana',
cityId: 'do_sdo',
cityName: 'Santo Domingo',
departmentId: 'dept_trade',
departmentName: 'Trade Marketing',
positionId: 'pos_imp',
positionName: 'Impulsador/a',
quantity: 8,
vacanciesFilled: 0,
modalidad: 'presencial',
tipoContratacion: 'temporal',
justification: 'Lanzamiento nuevo producto cliente principal en supermercados',
urgency: 'alta',
status: 'reclutando',
recruiterId: 'usr_002',
recruiterName: 'Carlos Méndez',
createdAt: '2024-01-19T13:00:00Z',
hardDeadline: '2024-02-10T23:59:59Z',
approvedByHrAt: '2024-01-19T17:30:00Z',
approvedByHrUserId: 'usr_005',
approvedByAdminAt: '2024-01-20T08:00:00Z',
approvedByAdminUserId: 'usr_006',
currentStage: 'primera_entrevista',
},
{
id: 'REQ-2024-008',
requesterId: 'usr_007',
requesterName: 'Elena Castro',
countryId: 'ar',
countryName: 'Argentina',
cityId: 'ar_bue',
cityName: 'Buenos Aires',
departmentId: 'dept_ops',
departmentName: 'Operaciones',
positionId: 'pos_super',
positionName: 'Supervisor',
quantity: 2,
vacanciesFilled: 0,
modalidad: 'presencial',
tipoContratacion: 'reemplazo',
justification: 'Reestructuración equipo operativo zona sur',
urgency: 'normal',
status: 'borrador',
createdAt: '2024-01-23T16:45:00Z',
hardDeadline: '2024-03-20T23:59:59Z',
},
{
id: 'REQ-2024-009',
requesterId: 'usr_010',
requesterName: 'Diego Hernández',
countryId: 'mx',
countryName: 'México',
cityId: 'mx_mty',
cityName: 'Monterrey',
departmentId: 'dept_trade',
departmentName: 'Trade Marketing',
positionId: 'pos_merch',
positionName: 'Merchandiser',
quantity: 15,
vacanciesFilled: 10,
modalidad: 'presencial',
tipoContratacion: 'nuevo',
justification: 'Ampliación cobertura tiendas de conveniencia región noreste',
urgency: 'alta',
status: 'reclutando',
recruiterId: 'usr_002',
recruiterName: 'Carlos Méndez',
createdAt: '2024-01-05T10:00:00Z',
hardDeadline: '2024-02-05T23:59:59Z',
approvedByHrAt: '2024-01-05T14:00:00Z',
approvedByHrUserId: 'usr_005',
approvedByAdminAt: '2024-01-06T09:00:00Z',
approvedByAdminUserId: 'usr_006',
currentStage: 'referencias',
},
{
id: 'REQ-2024-010',
requesterId: 'usr_008',
requesterName: 'Roberto Díaz',
countryId: 'gt',
countryName: 'Guatemala',
cityId: 'gt_gua',
cityName: 'Ciudad de Guatemala',
departmentId: 'dept_sales',
departmentName: 'Ventas',
positionId: 'pos_sales_jr',
positionName: 'Ejecutivo de Ventas Jr',
quantity: 3,
vacanciesFilled: 0,
modalidad: 'hibrido',
tipoContratacion: 'nuevo',
justification: 'Equipo comercial inicial para apertura de operación Guatemala',
urgency: 'critica',
status: 'cancelado',
createdAt: '2024-01-08T11:30:00Z',
hardDeadline: '2024-02-28T23:59:59Z',
approvedByHrAt: '2024-01-08T15:00:00Z',
approvedByHrUserId: 'usr_001',
closedAt: '2024-01-15T09:00:00Z',
closedReason: 'Proyecto de apertura Guatemala pospuesto hasta Q3 2024',
},
{
id: 'REQ-2024-011',
requesterId: 'usr_007',
requesterName: 'Elena Castro',
countryId: 'mx',
countryName: 'México',
cityId: 'mx_cdmx',
cityName: 'Ciudad de México',
departmentId: 'dept_mkt',
departmentName: 'Marketing',
positionId: 'pos_analyst',
positionName: 'Analista de Marketing',
quantity: 1,
vacanciesFilled: 0,
modalidad: 'hibrido',
tipoContratacion: 'nuevo',
justification: 'Apoyo para campaña de verano 2024',
urgency: 'alta',
status: 'pendiente_rrhh',
createdAt: new Date(Date.now() - 3 * 86400000).toISOString(),
hardDeadline: new Date(Date.now() + 15 * 86400000).toISOString(),
},
{
id: 'REQ-2024-012',
requesterId: 'usr_010',
requesterName: 'Diego Hernández',
countryId: 'co',
countryName: 'Colombia',
cityId: 'co_bog',
cityName: 'Bogotá',
departmentId: 'dept_it',
departmentName: 'Tecnología',
positionId: 'pos_analyst',
positionName: 'Soporte Técnico',
quantity: 2,
vacanciesFilled: 0,
modalidad: 'remoto',
tipoContratacion: 'reemplazo',
justification: 'Baja por renuncias en el equipo de soporte de Nivel 1',
urgency: 'normal',
status: 'pendiente_admin',
createdAt: new Date(Date.now() - 5 * 86400000).toISOString(),
hardDeadline: new Date(Date.now() + 30 * 86400000).toISOString(),
approvedByHrAt: new Date(Date.now() - 4 * 86400000).toISOString(),
approvedByHrUserId: 'usr_001',
},
{
id: 'REQ-2024-013',
requesterId: 'usr_008',
requesterName: 'Roberto Díaz',
countryId: 'pe',
countryName: 'Perú',
cityId: 'pe_lim',
cityName: 'Lima',
departmentId: 'dept_fin',
departmentName: 'Finanzas',
positionId: 'pos_analyst',
positionName: 'Analista Financiero',
quantity: 1,
vacanciesFilled: 0,
modalidad: 'hibrido',
tipoContratacion: 'nuevo',
justification: 'Control de presupuestos de las nuevas cuentas clave en Perú',
urgency: 'critica',
status: 'pendiente_rrhh',
createdAt: new Date(Date.now() - 1 * 86400000).toISOString(),
hardDeadline: new Date(Date.now() + 20 * 86400000).toISOString(),
},
]
// Recruiter Capacity
export const recruiterCapacity: RecruiterCapacity[] = [
{
recruiterId: 'usr_002',
recruiterName: 'Carlos Méndez',
countryFocus: ['México', 'Rep. Dominicana'],
currentScore: 22,
maxThreshold: 25,
openCases: 3,
tier1Count: 28,
tier2Count: 0,
tier3Count: 0,
},
{
recruiterId: 'usr_003',
recruiterName: 'Ana Rodríguez',
countryFocus: ['Colombia'],
currentScore: 12,
maxThreshold: 25,
openCases: 1,
tier1Count: 10,
tier2Count: 0,
tier3Count: 0,
},
{
recruiterId: 'usr_004',
recruiterName: 'Luis Torres',
countryFocus: ['Perú'],
currentScore: 5,
maxThreshold: 25,
openCases: 0,
tier1Count: 0,
tier2Count: 0,
tier3Count: 0,
},
{
recruiterId: 'usr_009',
recruiterName: 'Laura Martínez',
countryFocus: ['Chile', 'Argentina'],
currentScore: 8,
maxThreshold: 25,
openCases: 0,
tier1Count: 0,
tier2Count: 0,
tier3Count: 0,
},
]
// Notifications
export const notifications: Notification[] = [
{
id: 'notif_001',
userId: 'usr_001',
type: 'aprobacion',
title: 'Nueva requisición pendiente',
message: 'REQ-2024-003 requiere aprobación de RR.HH.',
requisitionId: 'REQ-2024-003',
read: false,
createdAt: '2024-01-22T11:25:00Z',
},
{
id: 'notif_002',
userId: 'usr_001',
type: 'sla_riesgo',
title: 'SLA en riesgo',
message: 'REQ-2024-007 tiene 2 días para fecha límite y 0 vacantes cubiertas.',
requisitionId: 'REQ-2024-007',
read: false,
createdAt: '2024-01-23T08:00:00Z',
},
{
id: 'notif_003',
userId: 'usr_001',
type: 'nueva_requisicion',
title: 'Requisición enviada',
message: 'Diego Hernández envió REQ-2024-002 para aprobación.',
requisitionId: 'REQ-2024-002',
read: true,
createdAt: '2024-01-20T08:50:00Z',
},
{
id: 'notif_004',
userId: 'usr_001',
type: 'transferencia',
title: 'Caso transferido',
message: 'REQ-2024-001 fue transferido a Carlos Méndez.',
requisitionId: 'REQ-2024-001',
read: true,
createdAt: '2024-01-18T14:00:00Z',
},
]
// Audit Log
export const auditLog: AuditLog[] = [
{
id: 'audit_001',
entityType: 'requisition',
entityId: 'REQ-2024-001',
userId: 'usr_007',
userName: 'Elena Castro',
action: 'Creación de requisición',
createdAt: '2024-01-15T10:30:00Z',
},
{
id: 'audit_002',
entityType: 'requisition',
entityId: 'REQ-2024-001',
userId: 'usr_007',
userName: 'Elena Castro',
action: 'Envío a aprobación',
oldValue: 'borrador',
newValue: 'pendiente_rrhh',
createdAt: '2024-01-15T10:35:00Z',
},
{
id: 'audit_003',
entityType: 'requisition',
entityId: 'REQ-2024-001',
userId: 'usr_005',
userName: 'Sofía Vargas',
action: 'Aprobación RR.HH.',
oldValue: 'pendiente_rrhh',
newValue: 'pendiente_admin',
createdAt: '2024-01-16T14:22:00Z',
},
{
id: 'audit_004',
entityType: 'requisition',
entityId: 'REQ-2024-001',
userId: 'usr_006',
userName: 'Pedro Silva',
action: 'Aprobación Administración',
oldValue: 'pendiente_admin',
newValue: 'reclutando',
createdAt: '2024-01-17T09:15:00Z',
},
{
id: 'audit_005',
entityType: 'requisition',
entityId: 'REQ-2024-001',
userId: 'usr_001',
userName: 'María González',
action: 'Asignación de reclutador',
newValue: 'Carlos Méndez',
createdAt: '2024-01-17T09:20:00Z',
},
]
// Helper functions
export function getRequisitionsByStatus(status: Requisition['status']) {
return requisitions.filter(r => r.status === status)
}
export function getRequisitionsByCountry(countryId: string) {
return requisitions.filter(r => r.countryId === countryId)
}
export function getCitiesByCountry(countryId: string) {
return cities.filter(c => c.countryId === countryId)
}
export function getCountryById(countryId: string) {
return countries.find(c => c.id === countryId)
}
export function getUserById(userId: string) {
return users.find(u => u.id === userId)
}
export function getRecruitersByCountry(countryId: string) {
return users.filter(u => u.role === 'reclutador' && (u.countryId === countryId || u.countryId === 'all'))
}
// Dashboard calculations
export function getDashboardStats() {
const now = new Date()
const open = requisitions.filter(r => !['cerrado', 'cancelado'].includes(r.status))
const pendingApproval = requisitions.filter(r => ['pendiente_rrhh', 'pendiente_admin'].includes(r.status))
const recruiting = requisitions.filter(r => r.status === 'reclutando')
const overdue = open.filter(r => new Date(r.hardDeadline) < now)
const atRisk = open.filter(r => {
const deadline = new Date(r.hardDeadline)
const daysLeft = Math.ceil((deadline.getTime() - now.getTime()) / (1000 * 60 * 60 * 24))
return daysLeft <= 7 && daysLeft > 0
})
const closedThisMonth = requisitions.filter(r => {
if (!r.closedAt) return false
const closedDate = new Date(r.closedAt)
return closedDate.getMonth() === now.getMonth() && closedDate.getFullYear() === now.getFullYear()
})
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1)
const closedLastMonth = requisitions.filter(r => {
if (!r.closedAt) return false
const closedDate = new Date(r.closedAt)
return closedDate.getMonth() === lastMonth.getMonth() && closedDate.getFullYear() === lastMonth.getFullYear()
})
// Calculate average TTF for closed requisitions
const closedWithTTF = requisitions.filter(r => r.status === 'cerrado' && r.closedAt)
const avgTTF = closedWithTTF.length > 0
? closedWithTTF.reduce((acc, r) => {
const created = new Date(r.createdAt)
const closed = new Date(r.closedAt!)
return acc + Math.ceil((closed.getTime() - created.getTime()) / (1000 * 60 * 60 * 24))
}, 0) / closedWithTTF.length
: 0
return {
totalOpen: open.length,
pendingApproval: pendingApproval.length,
recruiting: recruiting.length,
overdueCount: overdue.length,
atRiskCount: atRisk.length,
avgTTF: Math.round(avgTTF),
closedThisMonth: closedThisMonth.length,
closedLastMonth: closedLastMonth.length,
}
}
export const mockUsers = users
export const mockDepartments = departments
export const mockRequisitions = requisitions
export const mockNotifications = notifications
export const pipelineStages: import('./types').PipelineStage[] = [
{ id: 'applied', name: 'Aplicado' },
{ id: 'screening', name: 'Screening' },
{ id: 'interview_rrhh', name: 'Entrevista RRHH' },
{ id: 'interview_technical', name: 'Entrevista Técnica' },
{ id: 'interview_manager', name: 'Entrevista Manager' },
{ id: 'offer', name: 'Oferta' },
{ id: 'hired', name: 'Contratado' },
{ id: 'rejected', name: 'Rechazado' }
]
export const mockCandidates: import('./types').Candidate[] = [
{
id: 'cand_001',
name: 'Juan Perez',
email: 'juan@example.com',
phone: '555-0101',
requisitionId: 'REQ-2024-001',
currentStage: 'interview_rrhh',
currentPosition: 'Software Engineer',
currentCompany: 'Tech Corp',
rating: 4,
source: 'linkedin',
appliedDate: '2024-01-10T00:00:00Z'
}
]
export const mockInterviews: import('./types').Interview[] = [
{
id: 'int_001',
candidateId: 'cand_001',
requisitionId: 'REQ-2024-001',
scheduledDate: new Date(Date.now() + 86400000).toISOString(),
duration: 60,
type: 'technical',
location: 'video',
meetingLink: 'https://zoom.us/j/123',
interviewers: ['usr_001'],
status: 'scheduled'
}
]
+321
View File
@@ -0,0 +1,321 @@
// Types for GLM Requisition Portal
export type UserRole =
| 'solicitante'
| 'country_lead'
| 'rrhh_local'
| 'rrhh_regional'
| 'administracion'
| 'reclutador'
| 'director_rrhh'
| 'admin_sistema'
export type RequisitionStatus =
| 'borrador'
| 'pendiente_rrhh'
| 'pendiente_admin'
| 'correccion_requerida'
| 'reclutando'
| 'cerrado'
| 'cancelado'
export type CandidateStage =
| 'reclutando'
| 'primera_entrevista'
| 'segunda_entrevista'
| 'referencias'
| 'examenes_medicos'
| 'seleccionado'
| 'cerrado'
export type ComplexityTier = 1 | 2 | 3
export interface User {
id: string
email: string
name: string
role: UserRole
countryId: string
departmentId?: string
avatar?: string
active: boolean
}
export interface Country {
id: string
name: string
code: string
timezone: string
active: boolean
}
export interface City {
id: string
countryId: string
name: string
}
export interface Department {
id: string
name: string
countryId: string
}
export interface Position {
id: string
name: string
complexityTier: ComplexityTier
}
export interface SlaRule {
id: string
countryId: string
cityId?: string
positionId: string
baseDays: number
incrementalFactor: number
}
export interface Requisition {
id: string
legacyId?: string
requesterId: string
requesterName: string
countryId: string
countryName: string
cityId: string
cityName: string
departmentId: string
departmentName: string
positionId: string
positionName: string
quantity: number
vacanciesFilled: number
modalidad: 'presencial' | 'remoto' | 'hibrido'
tipoContratacion: 'nuevo' | 'reemplazo' | 'temporal'
justification: string
urgency: 'normal' | 'alta' | 'critica'
salaryPackage?: string
budget?: number
currency?: string
status: RequisitionStatus
recruiterId?: string
recruiterName?: string
createdAt: string
hardDeadline: string
approvedByHrAt?: string
approvedByHrUserId?: string
approvedByAdminAt?: string
approvedByAdminUserId?: string
closedAt?: string
closedReason?: string
rejectionComment?: string
attachments?: string[]
currentStage?: CandidateStage
}
export interface ApprovalEvent {
id: string
requisitionId: string
approverId: string
approverName: string
approvalType: 'rrhh' | 'administracion'
decision: 'aprobado' | 'rechazado'
comment?: string
createdAt: string
}
export interface AuditLog {
id: string
entityType: 'requisition' | 'user' | 'candidate'
entityId: string
userId: string
userName: string
action: string
oldValue?: string
newValue?: string
createdAt: string
}
export interface RecruiterAssignment {
id: string
requisitionId: string
recruiterId: string
recruiterName: string
assignedAt: string
assignedBy: string
active: boolean
}
export interface RecruiterCapacity {
recruiterId: string
recruiterName: string
avatar?: string
countryFocus: string[]
currentScore: number
maxThreshold: number
openCases: number
tier1Count: number
tier2Count: number
tier3Count: number
}
export interface DashboardStats {
totalOpen: number
pendingApproval: number
recruiting: number
overdueCount: number
atRiskCount: number
avgTTF: number
closedThisMonth: number
closedLastMonth: number
}
export interface Notification {
id: string
userId: string
type: 'nueva_requisicion' | 'aprobacion' | 'rechazo' | 'asignacion' | 'sla_riesgo' | 'transferencia'
title: string
message: string
requisitionId?: string
read: boolean
createdAt: string
}
// Status configuration for UI
export const STATUS_CONFIG: Record<RequisitionStatus, {
label: string
color: string
bgColor: string
borderColor: string
}> = {
borrador: {
label: 'Borrador',
color: 'text-gray-700 dark:text-gray-300',
bgColor: 'bg-gray-100 dark:bg-gray-800',
borderColor: 'border-gray-300 dark:border-gray-600'
},
pendiente_rrhh: {
label: 'Pendiente RR.HH.',
color: 'text-amber-700 dark:text-amber-300',
bgColor: 'bg-amber-50 dark:bg-amber-950',
borderColor: 'border-amber-300 dark:border-amber-700'
},
pendiente_admin: {
label: 'Pendiente Administración',
color: 'text-purple-700 dark:text-purple-300',
bgColor: 'bg-purple-50 dark:bg-purple-950',
borderColor: 'border-purple-300 dark:border-purple-700'
},
correccion_requerida: {
label: 'Corrección Requerida',
color: 'text-orange-700 dark:text-orange-300',
bgColor: 'bg-orange-50 dark:bg-orange-950',
borderColor: 'border-orange-300 dark:border-orange-700'
},
reclutando: {
label: 'Reclutando',
color: 'text-sky-700 dark:text-sky-300',
bgColor: 'bg-sky-50 dark:bg-sky-950',
borderColor: 'border-sky-300 dark:border-sky-700'
},
cerrado: {
label: 'Cerrado',
color: 'text-emerald-700 dark:text-emerald-300',
bgColor: 'bg-emerald-50 dark:bg-emerald-950',
borderColor: 'border-emerald-300 dark:border-emerald-700'
},
cancelado: {
label: 'Cancelado',
color: 'text-red-700 dark:text-red-300',
bgColor: 'bg-red-50 dark:bg-red-950',
borderColor: 'border-red-300 dark:border-red-700'
},
}
export const STAGE_CONFIG: Record<CandidateStage, {
label: string
order: number
}> = {
reclutando: { label: 'Búsqueda Activa', order: 1 },
primera_entrevista: { label: '1era Entrevista', order: 2 },
segunda_entrevista: { label: '2da Entrevista', order: 3 },
referencias: { label: 'Referencias', order: 4 },
examenes_medicos: { label: 'Exámenes Médicos', order: 5 },
seleccionado: { label: 'Seleccionado', order: 6 },
cerrado: { label: 'Cerrado', order: 7 },
}
export const URGENCY_CONFIG = {
normal: { label: 'Normal', color: 'text-gray-600', bgColor: 'bg-gray-100' },
alta: { label: 'Alta', color: 'text-amber-600', bgColor: 'bg-amber-100' },
critica: { label: 'Crítica', color: 'text-red-600', bgColor: 'bg-red-100' },
}
export const COMPLEXITY_CONFIG: Record<ComplexityTier, {
label: string
description: string
points: number
}> = {
1: { label: 'Tier 1', description: 'Operativo (Merchandisers, Promotores)', points: 1 },
2: { label: 'Tier 2', description: 'Especializado (Ventas, Analistas)', points: 3 },
3: { label: 'Tier 3', description: 'Liderazgo (Supervisores, Managers)', points: 5 },
}
export interface PipelineStage {
id: string
name: string
}
export interface Candidate {
id: string
name: string
email: string
phone: string
avatar?: string
requisitionId: string
currentStage: string
currentPosition?: string
currentCompany?: string
rating?: number
source: string
appliedDate: string
linkedin?: string
salaryExpectation?: string
availability?: string
notes?: string
history?: Array<{
type: string
title: string
description: string
date: string
user?: string
}>
evaluations?: Array<{
stage: string
evaluator: string
date: string
recommendation: string
score: number
comments: string
}>
documents?: Array<{
name: string
uploadedAt: string
url: string
}>
}
export interface Interview {
id: string
candidateId: string
requisitionId: string
scheduledDate: string
duration: number
type: string
location: string
meetingLink?: string
interviewers: string[]
notes?: string
status: string
}
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}