05:30 · antes del primer relevo
Vigtrack
Control de asistencia y novedades para empresas de vigilancia privada
Una jornada entera —de la primera entrada al cierre del reporte— dentro de una sola aplicación web, con el rastro de quién decidió qué y cuándo.
Sistema interno de una empresa de vigilancia · sin acceso público
Toda una operación dentro de un Excel
Decenas de sedes, varias regiones y una única cuadrícula que todos miraban igual.
| A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|
| Categoría | 01 | 02 | 03 | 04 | 05 | 06 |
| Ausencias | 3 | 1 | 7 | 2 | ||
| Vacaciones | 12 | 12 | 11 | 11 | 10 | 10 |
| Egresos | 2 | 1 | ||||
| Reasignados | 4 | 3 | 6 | |||
| Sin cubrir | 1 | 2 |
Una empresa de vigilancia privada con oficiales repartidos en decenas de sedes y varias regiones controlaba la asistencia con hojas de cálculo: un parte numérico mensual de categorías por día, listados de ausencias, egresos, ingresos, reasignaciones y vacaciones en pestañas separadas. Nadie podía saber en el momento cuántos puestos estaban descubiertos, quién tenía justificativo pendiente ni en qué manos estaba la decisión sobre una incidencia. Cada rol —supervisor en campo, analista, coordinador, administración— necesitaba una vista distinta de la misma realidad, y el Excel obligaba a todos a mirar la misma cuadrícula.
Cada rol mira su propia realidad
Vigtrack traduce esa operación a una aplicación web con control de acceso por rol. El supervisor abre la jornada del día sobre un roster regional que asume presencia por defecto y solo pide marcar la novedad; el analista revisa justificativos y valida inconsistencias; el coordinador aprueba o rechaza y la decisión queda registrada con observación y fecha; el administrador mantiene los catálogos (clientes, sucursales, regiones, tipos de servicio, estatus de oficial, tipos de novedad) y consulta el histórico nacional. El parte numérico deja de ser una hoja y se convierte en un mapa de calor navegable por mes con exportación a CSV. Todo ello con tema claro/oscuro, interfaz bilingüe español/inglés y navegación inferior propia en móvil.
Un solo login, cinco aplicaciones
El mismo dominio se comporta como cinco productos distintos: menú, dashboard y rutas protegidas propias.
Vigtrack
Sistema de Gestión de Seguridad
Bearer inyectado por interceptor · 401 ⇒ logout
Login
Mantiene los catálogos y mira el histórico nacional.
Abre la jornada y registra lo que pasa en campo.
Revisa justificativos y cruza inconsistencias.
Decide: escala a RRHH o muere en auditoría.
Vigila el día entero desde una sola pantalla.
Cinco piezas que no se pueden saltar
Cinco roles, cinco aplicaciones
admin, supervisor, analyst, coordinator y coordination_center comparten login y layout, pero cada uno recibe su menú lateral, su barra inferior móvil y su dashboard. Los guards RequireAuth y RequireRole cierran cada ruta a la lista de roles que la pueden ver.
Jornada con estado explícito
La asistencia diaria no es una tabla editable suelta: la jornada pasa por sin abrir, abierta y cerrada. Cerrar dispara una confirmación y envía el reporte al coordinador; reabrir exige otra confirmación. El estado es visible como pastilla en la cabecera.
Incidencias con tres firmas
Cada novedad recorre pendiente de análisis → analizada → aprobada (escala a RRHH) o rechazada (queda en auditoría). El modal de detalle dibuja el recorrido paso a paso con el rol responsable y la fecha de cada decisión.
El parte numérico como mapa de calor
Veinte categorías por treinta días en una cuadrícula con columna fija, leyenda de rangos (0, 1–5, 6–15, 16+) y navegador de mes. Sustituye la hoja de cálculo original sin perder la lectura de un vistazo.
Bilingüe, con tema y sesión persistidos
191 claves de traducción en español e inglés, tema claro/oscuro/sistema conmutado desde la barra superior y redux-persist guardando auth, tema e idioma en el navegador para que la sesión sobreviva a la recarga.
El recorrido de una incidencia
Registrar no es decidir. Entre el campo y RRHH hay tres firmas, y todas quedan con fecha.
Registra la novedad en campo
01/04 · 09:45
Revisa el justificativo
01/04 · 15:20
Decisión final con observación
02/04 · 08:05
Aprobada · escalada a RRHH
La decisión sale del sistema operativo y entra en el expediente del oficial.
Rechazada · auditoría
La incidencia muere aquí, pero queda registrada con su observación y su fecha.
El modal que dibuja el expediente completo
Los datos del evento arriba en pares etiqueta-valor y, debajo, la línea vertical de pasos: círculo relleno con check para lo hecho, círculo hueco para lo pendiente y círculo rojo con aspa cuando la incidencia se rechaza. Al pie, aprobar en verde, rechazar en rojo contorneado y una observación que no se puede dejar vacía.
Detalle de novedad con línea de aprobación
La jornada se abre.
A partir de aquí todo ocurre a la luz del día: el roster, el parte numérico, la plantilla del mes. Las mismas pantallas que sustituyeron a la hoja de cálculo.
Las pantallas donde se trabaja
Recreadas aquí en HTML y CSS, con los mismos colores, pastillas y disposición del producto.
Asistencia diaria
Región Miranda · presente-por-defecto: todos laboran salvo novedad
Asistencia diaria del supervisor
Presente por defecto: solo se marca la excepción.
Región Miranda · decisión final tras el análisis. El Analista analiza primero; aquí apruebas (escala a RRHH) o rechazas (muere en auditoría).
Incidencias — decisión del coordinador
Una sola ruta, tres experiencias según el rol del store.
Las maquetas reproducen la interfaz real; los datos que muestran son de ejemplo.
La hoja de cálculo, convertida en color
Veinte categorías por treinta días y un damero de turnos de 24 horas: dos cuadrículas que se leen de un vistazo y se exportan a CSV.
Parte Numérica
20 categorías · 30 días
| Categoría | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Ausencia injustificada | 6 | 11 | 16 | 21 | 3 | 8 | 18 | 23 | 5 | 10 | 15 | 20 | 7 | 12 | 17 | 22 | 4 | 9 | 19 | 1 | 6 | 11 | 16 | 21 | 8 | |||||
| Reposo médico | 8 | 14 | 20 | 9 | 15 | 21 | 4 | 10 | 16 | 5 | 11 | 17 | 23 | 6 | 12 | 1 | 7 | 13 | 19 | 2 | 8 | 20 | 3 | 9 | 15 | 21 | ||||
| Cita médica | 15 | 22 | 6 | 13 | 20 | 4 | 18 | 2 | 9 | 16 | 23 | 7 | 21 | 5 | 12 | 19 | 3 | 10 | 1 | 8 | 15 | 22 | 6 | 13 | 4 | 11 | ||||
| Vacaciones | 22 | 7 | 23 | 8 | 16 | 1 | 9 | 17 | 10 | 18 | 3 | 11 | 19 | 4 | 20 | 5 | 13 | 21 | 6 | 14 | 7 | 15 | 23 | 8 | 16 | 1 | ||||
| Permiso legal | 6 | 15 | 1 | 10 | 19 | 14 | 23 | 9 | 18 | 4 | 13 | 8 | 17 | 3 | 12 | 21 | 7 | 2 | 11 | 20 | 6 | 15 | 1 | 19 | 5 | 14 | ||||
| Ingreso de personal | 13 | 10 | 20 | 7 | 17 | 4 | 14 | 11 | 21 | 8 | 18 | 5 | 15 | 12 | 22 | 9 | 19 | 6 | 16 | 13 | 23 | 10 | 20 | 7 | 17 | |||||
| Egreso de personal | 20 | 8 | 19 | 7 | 6 | 17 | 5 | 16 | 4 | 15 | 14 | 2 | 13 | 1 | 12 | 23 | 22 | 10 | 21 | 9 | 20 | 8 | 7 | 18 | 6 | 17 | ||||
| Reasignación | 16 | 5 | 17 | 6 | 18 | 7 | 8 | 20 | 9 | 21 | 10 | 22 | 23 | 12 | 1 | 13 | 2 | 14 | 15 | 4 | 16 | 5 | 17 | 6 | 7 | |||||
| Cobertura por backup | 11 | 1 | 14 | 17 | 7 | 20 | 10 | 23 | 13 | 16 | 6 | 19 | 9 | 22 | 12 | 15 | 5 | 18 | 8 | 21 | 11 | 14 | 4 | 17 | 7 | 20 | ||||
| Puesto sin cubrir | 18 | 9 | 23 | 14 | 5 | 19 | 1 | 15 | 6 | 20 | 11 | 2 | 7 | 21 | 12 | 3 | 17 | 8 | 13 | 4 | 18 | 9 | 23 | 14 | 19 | 10 | ||||
| Retardo | 2 | 17 | 1 | 16 | 8 | 23 | 15 | 7 | 14 | 6 | 21 | 13 | 5 | 20 | 4 | 19 | 11 | 3 | 18 | 10 | 17 | 9 | 1 | 16 | 8 | 23 | ||||
| Cambio de turno | 9 | 2 | 18 | 11 | 4 | 13 | 6 | 22 | 15 | 8 | 1 | 10 | 3 | 19 | 12 | 5 | 21 | 7 | 23 | 16 | 9 | 2 | 18 | 4 | 20 | 13 | ||||
| Suspensión | 16 | 4 | 21 | 15 | 9 | 3 | 20 | 8 | 2 | 19 | 13 | 7 | 1 | 12 | 6 | 23 | 17 | 11 | 5 | 16 | 10 | 4 | 21 | 15 | 9 | |||||
| Servicio extra | 23 | 18 | 13 | 8 | 21 | 16 | 11 | 6 | 1 | 19 | 9 | 4 | 22 | 17 | 12 | 7 | 20 | 15 | 10 | 5 | 23 | 18 | 8 | 3 | 21 | 16 | ||||
| Capacitación | 3 | 22 | 18 | 14 | 10 | 6 | 21 | 17 | 13 | 9 | 5 | 1 | 16 | 12 | 8 | 4 | 23 | 19 | 11 | 7 | 3 | 22 | 18 | 14 | 6 | |||||
| Incidencia disciplinaria | 14 | 11 | 8 | 2 | 22 | 19 | 16 | 13 | 10 | 4 | 1 | 21 | 18 | 15 | 12 | 6 | 3 | 23 | 20 | 17 | 14 | 8 | 5 | 2 | 22 | 19 | ||||
| Abandono de puesto | 21 | 19 | 17 | 15 | 13 | 11 | 7 | 5 | 3 | 1 | 22 | 20 | 16 | 14 | 12 | 10 | 8 | 6 | 2 | 23 | 21 | 19 | 17 | 15 | 11 | 9 | ||||
| Traslado temporal | 5 | 4 | 2 | 1 | 23 | 22 | 21 | 20 | 18 | 17 | 16 | 15 | 14 | 13 | 11 | 10 | 9 | 8 | 7 | 6 | 4 | 3 | 2 | 1 | 23 | 22 | ||||
| Reposo post-vacacional | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | ||||
| Renuncia | 19 | 21 | 22 | 23 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 1 |
Parte numérica (mapa de calor)
Plantilla mensual
30 días por oficial · damero de 24 horas
| Oficial | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Nieves, José G.GRP-A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L |
| Rangel, María C.GRP-B | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h |
| Chirinos, Ander J.GRP-A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L |
| Bastidas, Luis E.GRP-C | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L |
| Mendoza, Rosa M.GRP-B | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | V | V | V | V | V | V | V | L | 24h | L | 24h | L | 24h |
| Salazar, Deivis A.GRP-C | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L |
| Pacheco, Eglee R.GRP-A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L |
| Ojeda, Carlos D.GRP-B | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | A | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h | L | 24h |
Plantilla mensual
Columna fija
position sticky con fondo propio en claro y oscuro: al desplazarse treinta días no se pierde de vista la fila.
Color por rango
0, 1–5, 6–15 y 16+ con su leyenda, para interpretar la cuadrícula sin leer un solo número.
CSV con BOM UTF-8
El utilitario exportCsv escribe el BOM, escapa comillas y usa CRLF: Excel en español respeta ñ y tildes.
El tamaño real de la maqueta
25
rutas registradas
15
módulos feature-first
24
páginas con carga diferida
5
roles con navegación propia
6
dashboards por rol
42
funciones de API tipadas
11
recursos REST consumidos
2
idiomas (es / en)
191
claves de traducción
9
tipos de evento operativo
Quince marcas en la misma jornada
Cada funcionalidad, fichada a su hora. Es todo lo que hoy hace la aplicación.
Login con email y contraseña contra la API real, token Bearer inyectado por interceptor y expulsión automática al recibir un 401
Dashboards diferenciados por rol con tarjetas KPI (oficiales activos, presentes, con novedad, cubiertos por backup, puestos sin cubrir, vacaciones, reasignaciones)
Asistencia diaria por región con filtros Todos / Sin novedad / Con novedad, navegador de fecha y apertura y cierre de jornada
Plantilla mensual de 30 días por oficial con celdas de estado (24h, L de libre, A de ausencia, V de vacaciones) y columna de nombre fija al hacer scroll horizontal
Módulo de novedades y eventos con nueve tipos (ingreso, egreso, reasignación, vacación, ausencia, reposo médico, cobertura, incidencia y asignación de supervisor)
Flujo de incidencias con vistas separadas para coordinador, analista y centro de coordinación, modales de decisión y de detalle, y filtros por cliente, motivo y estado
Justificativos, reasignaciones y validación cruzada de inconsistencias para el analista de operaciones
Asignación de supervisores a sedes con selección múltiple, buscador de sede y aplicación inmediata o programada a una fecha
CRUD de personal con búsqueda con debounce, filtros por estatus, cliente y sede, cambio de estatus mediante modal y paginación servidor
Catálogos en pestañas: tipos de novedad e incidencia, estatus de oficiales, tipos de servicio y regiones
Parte numérica, resumen por región y reporte por cliente, todos exportables a CSV con BOM UTF-8 para que Excel respete los acentos
Tablero de alertas con severidad alta, media y baja, marcado individual y marcar todo como leído
Centro de notificaciones con feed, cajón lateral y contador de no leídas
Tema claro, oscuro o del sistema y conmutador de idioma es/en desde la barra superior
Layout responsive: barra lateral fija en escritorio, cajón deslizante y navegación inferior por rol en móvil
El cuadro de turnos técnico
Feature-first, y un solo contrato
Quince módulos autónomos, una capa HTTP tipada y un mock poblado con la exportación real del Excel.
type ApiResult<T> =
| { ok: true; data: T }
| { ok: false; status; detail; fieldErrors }
Arquitectura feature-first sobre Vite + SWC. En src/app viven el bootstrap (App, routes y providers); en src/shared, todo lo transversal: la capa api (cliente Axios con interceptores de request y response, handlers y el tipo ApiResult<T> que devuelve {ok:true,data} o {ok:false,status,detail,fieldErrors}), los componentes reutilizables agrupados en ui, data, form, feedback, pagination y layout, los hooks (usePagination, useApi, useDebounce, useT, useAuth, useTheme, useMediaQuery), el store de Redux Toolkit con seis slices (auth, theme, lang, ui, notifications e incidencias), la i18n con dos ficheros de locales y las utilidades (notify, exportCsv, applyServerErrors, cn). En src/features hay quince módulos autónomos —auth, dashboard, personal, attendance, events, incidencias, justifications, reports, assignments, catalogs, users, admin, operator, notifications y profile— cada uno con sus carpetas pages, components, api, schemas, types, hooks y store según necesite. Las veinticinco rutas se declaran en un único routes.tsx con carga diferida por página y se envuelven en guards RequireAuth y RequireRole. El cliente HTTP compone la baseURL con las variables VITE_PUBLIC_URL_API y VITE_PUBLIC_URL_VERSION, añade el token del store y la cabecera Accept-Language según el idioma activo, fuerza logout ante un 401 fuera del login y levanta un modal de error ante cualquier 5xx. Once módulos de API cubren cuarenta y dos operaciones tipadas sobre once recursos REST (auth, users, clients, sites, regions, officers, positions, incident-types, officer-statuses, service-types y events); los módulos aún no migrados leen del mock DB de src/shared/mock/db.ts, poblado con la exportación real del Excel operativo, de modo que el cambio de mock a red no toca los componentes.
Los quince módulos
42
funciones de API tipadas
11
recursos REST
6
slices de Redux
Cinco novedades del propio proyecto
Registradas, analizadas y resueltas — el mismo ciclo que aplica la aplicación, aplicado al código.
El mismo dominio tenía que comportarse como cinco productos distintos sin duplicar el layout ni dejar rutas abiertas por descuido.
Un único AppLayout con Sidebar y Topbar, y un mapa navByRole que define la navegación de cada rol. Las rutas se envuelven en RequireRole con la lista explícita de roles permitidos, y páginas como Incidencias delegan en un componente distinto según el rol leído del store, de modo que una sola ruta sirve tres experiencias.
Diseñar contra datos ficticios habría producido pantallas que se rompen con la operación real: nombres largos, decenas de sedes, meses completos de parte numérico.
Se exportó el Excel operativo a un mock tipado con ochenta y siete oficiales, setenta y un ausencias, cincuenta vacaciones, veintiocho ingresos, veintitrés egresos, veintidós reasignaciones y veinte categorías de parte numérico. Cada tabla, mapa de calor y modal se ajustó contra ese volumen antes de existir el backend.
Había que empezar a consumir la API real sin bloquear el desarrollo de los módulos que todavía no tenían endpoint.
Todos los servicios devuelven el mismo contrato ApiResult<T> y las páginas consumen datos a través de hooks (useApi, usePagination), no de Axios directamente. Eso permitió migrar auth, usuarios, clientes, sucursales, oficiales, catálogos y eventos uno a uno mientras asistencia, plantilla, reportes y alertas seguían leyendo del mock, sin reescribir componentes.
Una cuadrícula de treinta días por oficial o por categoría se vuelve ilegible en cuanto se hace scroll horizontal: se pierde de vista a quién pertenece la fila.
Las tablas de plantilla y parte numérica fijan la primera columna con position sticky y fondo propio en claro y oscuro, usan celdas de ancho fijo y color por estado, y añaden una leyenda de rangos para que el color se pueda interpretar sin leer los números.
Exportar a CSV en un entorno donde todo se abre con Excel en español rompía los acentos de nombres y motivos.
El utilitario exportCsv escribe un BOM UTF-8 al inicio del blob, escapa comillas dobles y usa saltos CRLF, de forma que Excel interpreta correctamente ñ, tildes y comas dentro de los campos.
El color nunca decora: informa
Sobrio, institucional y operativo — azul corporativo sobre gris azulado frío, con una barra lateral casi negra (#0f172a) como ancla visual. El color nunca decora: solo informa. La escala semántica completa es ok #16a34a sobre #dcfce7, warn #d97706 sobre #fef3c7, crit #dc2626 sobre #fee2e2 e info #0891b2 sobre #cffafe. Bordes #e2e8f0, texto secundario #64748b, terciario #94a3b8, radio de 10px y sombras muy suaves.
ok
presente · aprobada
warn
novedad · por decidir
crit
sin cubrir · rechazada
info
informativo · relevo
Favicon SVG que index.html sirve actualmente: un rayo morado (#863bff) con degradados difuminados heredado de la plantilla de arranque. No es una marca propia de Vigtrack — el proyecto todavía no tiene logotipo definitivo.
Vigtrack es el panel web con el que una empresa de seguridad privada deja de llevar la asistencia de sus oficiales en hojas de Excel. Cada día, cada región y cada puesto quedan registrados en una misma aplicación, con un flujo de aprobación que se puede auditar.
La aplicación no es un único tablero: son cinco aplicaciones distintas bajo el mismo dominio. Administrador, supervisor regional, analista de operaciones, coordinador y centro de coordinación entran con el mismo login y reciben su propio menú lateral, su propio dashboard y su propio conjunto de rutas protegidas.
El núcleo del sistema es el ciclo de una novedad: el supervisor la registra en campo, el analista revisa el justificativo, el coordinador decide, y el resultado escala a RRHH o muere en auditoría. Ese recorrido se muestra como una línea de pasos con fechas dentro del detalle de cada evento.
Está construido feature-first sobre React 18, TypeScript en modo estricto y Vite: quince módulos independientes, veinticinco rutas registradas con carga diferida, Redux Toolkit con persistencia, formularios validados con Zod y una capa HTTP tipada que devuelve siempre un ApiResult.
Los datos de la maqueta no son inventados: provienen de la exportación real del Excel operativo (parte CECOM y personal activo), lo que permitió diseñar cada pantalla contra volúmenes y casos reales antes de conectar el backend.
Vigtrack
¿Quieres algo así para tu producto?
Una operación completa —cinco roles, un ciclo de aprobación auditable y las cuadrículas que sustituyen al Excel— dentro de una sola aplicación React. Si tu empresa lleva su control en hojas de cálculo, éste es exactamente el terreno que conozco.