Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/danitocsc/transporte-unrc-web-public/llms.txt

Use this file to discover all available pages before exploring further.

La página /informe de Transporte Tijuana presenta el análisis de demanda estudiantil mediante tres familias de componentes de visualización: gráficas de barras interactivas con Recharts, reproducción de video en streaming HLS y un mapa de calor geográfico con MapLibre GL. Todos son componentes 'use client' que se hidratan en el navegador después de la renderización SSR del servidor.

ChartCard

ChartCard renderiza una gráfica de barras responsiva dentro de una tarjeta con encabezado y chips de resumen. Se usa en /informe para visualizar tres series de datos: distribución por turno, días de asistencia y colonias de origen de los estudiantes encuestados.

Props

type ChartCardProps = {
  title: string;
  subtitle: string;
  data: SeriesRow[];
  colors: string[];
  layout?: 'horizontal' | 'vertical';
};
title
string
required
Título principal de la tarjeta, renderizado como <h3> dentro del encabezado del panel.
subtitle
string
required
Texto descriptivo secundario que aparece junto al título, explicando la fuente o el contexto de la serie.
data
SeriesRow[]
required
Arreglo de filas de datos de la serie. Proviene de summary.series.turnos, summary.series.dias o summary.series.colonias. Ver SeriesRow.
colors
string[]
required
Arreglo de colores hexadecimales para las barras. Los colores se asignan en orden cíclico (colors[index % colors.length]) usando el componente <Cell> de Recharts.
layout
'horizontal' | 'vertical'
Orientación de la gráfica. Por defecto 'horizontal'. Usar 'vertical' para series con etiquetas largas (como colonias), ya que coloca las categorías en el eje Y con ancho calculado automáticamente.

Comportamiento según layout

<ChartCard
  title="Distribución por Turno"
  subtitle="Respuestas por turno académico"
  data={summary.series.turnos}
  colors={['#630038', '#e11d48', '#fb7185']}
  layout="horizontal"
/>
Internamente, ChartCard mapea cada SeriesRow a un objeto con campos label, value y percentLabel para el tooltip:
const chartData = data.map((row) => ({
  ...row,
  label: row.Categoria,
  value: row.Cantidad,
  percentLabel: `${row.Cantidad} (${row.Porcentaje.toFixed(1)}%)`,
}));
La altura de la gráfica se ajusta dinámicamente: en layout vertical toma Math.max(460, chartData.length * 32) píxeles; en horizontal usa 320 px fijos. El ancho del eje Y en modo vertical se calcula como Math.max(140, ...chartData.map((r) => r.label.length * 8)) para acomodar etiquetas largas sin truncamiento.

Chips de resumen

Debajo de la gráfica se renderizan hasta 8 chips con el formato "{Categoria}: {Cantidad} ({Porcentaje}%)" usando la clase CSS metric-chip:
<div className="chip-row">
  {chartData.slice(0, 8).map((row) => (
    <span key={row.label} className="metric-chip">
      {row.label}: {row.percentLabel}
    </span>
  ))}
</div>

VideoPlayer

VideoPlayer reproduce video en streaming HLS (.m3u8) en la página /informe. Se usa para mostrar contexto periodístico sobre la demanda de los estudiantes de UNRC Tijuana de un puente peatonal y transporte universitario.

Props

type VideoPlayerProps = {
  src: string;
  poster?: string;
};
src
string
required
URL del manifiesto HLS (archivo .m3u8). La URL es cargada por hls.js en navegadores que no soportan HLS nativo, o asignada directamente a video.src en Safari/iOS.
poster
string
URL de la imagen de portada que se muestra antes de que el usuario reproduzca el video.

Lógica de reproducción HLS

El componente detecta en tiempo de ejecución cuál método de reproducción usar:
useEffect(() => {
  const video = videoRef.current;
  if (!video) return;

  let hls: Hls | null = null;

  if (Hls.isSupported()) {
    // Chrome, Firefox, Edge — hls.js carga el stream y lo alimenta al elemento <video>
    hls = new Hls({
      maxMaxBufferLength: 10,
      enableWorker: true,
    });
    hls.loadSource(src);
    hls.attachMedia(video);
  } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
    // Safari e iOS tienen soporte HLS nativo
    video.src = src;
  }

  return () => {
    if (hls) hls.destroy(); // Limpieza del worker al desmontar
  };
}, [src]);
El elemento <video> resultante usa controls, playsInline y object-contain sobre fondo negro para una presentación consistente en todos los dispositivos.
maxMaxBufferLength: 10 limita el buffer máximo a 10 segundos para reducir el consumo de memoria en dispositivos con RAM limitada, como teléfonos de gama baja comunes entre los estudiantes.

DashboardMapLoader

DashboardMapLoader es el componente envolvente que carga DashboardMap de forma diferida con dynamic({ ssr: false }). Su única responsabilidad es evitar que el componente del mapa sea incluido en el bundle SSR del servidor.

Props

type DashboardMapLoaderProps = {
  data: MapPayload;
};
data
MapPayload
required
Payload del mapa de calor cargado por loadSiteData(). Contiene el arreglo de puntos geolocalizados y las coordenadas del centro. Ver MapPayload.

Implementación

const DashboardMapInner = dynamic(
  () => import('@/components/dashboard-map').then((m) => m.DashboardMap),
  {
    ssr: false,
    loading: () => <div className="map-loading">Cargando mapa...</div>,
  }
);

export function DashboardMapLoader({ data }: DashboardMapLoaderProps) {
  return <DashboardMapInner data={data} />;
}
Mientras el bundle de MapLibre GL se descarga y el mapa se inicializa, se muestra el placeholder <div className="map-loading">Cargando mapa...</div> que la hoja de estilos del informe convierte en un indicador de carga visual.

DashboardMap

DashboardMap es el componente de mapa de calor que visualiza la distribución geográfica de la demanda de transporte. Usa MapLibre GL JS con su capa de tipo heatmap nativa para representar la densidad de puntos de origen de los estudiantes encuestados.

Props

type DashboardMapProps = {
  data: MapPayload;
};
data
MapPayload
required
Objeto con la lista de puntos geolocalizados y las coordenadas del centro del mapa. El componente usa data.center para centrar la vista inicial y data.points para construir la fuente GeoJSON del heatmap.

Inicialización del mapa

El mapa se crea en useEffect de montado, centrado en data.center (que viene en formato [lat, lng]) con las coordenadas invertidas a [lng, lat] para MapLibre GL:
const map = new maplibregl.Map({
  container: containerRef.current,
  style: MAP_STYLE, // CartoDB Voyager raster tiles
  center: [data.center[1], data.center[0]], // [lat, lng] → [lng, lat]
  zoom: 12,
});

Construcción del GeoJSON del heatmap

Cada PublicPoint del arreglo data.points se convierte en un Feature GeoJSON con peso uniforme de 0.8:
function buildHeatGeoJSON(points: PublicPoint[]): GeoJSON.FeatureCollection {
  return {
    type: 'FeatureCollection',
    features: points.map((p) => ({
      type: 'Feature',
      properties: { weight: 0.8 },
      geometry: {
        type: 'Point',
        coordinates: [p.lon_publica, p.lat_publica],
      },
    })),
  };
}

Capa de heatmap

La capa heat-layer de tipo heatmap usa una rampa de color continua de amarillo pálido a rojo intenso:
DensidadColor
0.0Transparente
0.2#ffffb2 (amarillo muy pálido)
0.4#fed976 (amarillo)
0.6#feb24c (naranja claro)
0.8#fd8d3c (naranja)
0.9#fc4e2a (naranja-rojo)
1.0#bd0026 (rojo oscuro)
El radio del heatmap es 22 px, con intensidad 1 y opacidad 0.7 para permitir ver el mapa base debajo de las zonas de alta densidad.

Marcador UNRC

Se añade un marcador fijo rojo (#dc2626) en las coordenadas de la universidad [-116.860092, 32.436451] con un popup que identifica "UNRC Unidad Tijuana — Universidad Nacional Rosario Castellanos".

Leyenda

El componente incluye una leyenda en HTML fuera del canvas del mapa (div.map-legend) con:
  • Un gradiente visual que representa la rampa de densidad baja → alta.
  • Un indicador del marcador de UNRC.
La altura del contenedor del mapa es 520 px con border-radius: 16px.

Build docs developers (and LLMs) love