Initial commit: Proyecto portal-requisicion-vacantes
This commit is contained in:
@@ -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
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from 'clsx'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
Reference in New Issue
Block a user