Cargando…

Esto está tardando más de lo esperado.

Volver al centro de ayuda

Métricas del dashboard y seguimiento de visitantes

Qué cuenta cada gráfica de /admin, de dónde salen sus números, cómo agregar una y cómo el seguimiento de visitantes alimenta a Facebook.

El dashboard de administración dibuja una gráfica a la vez sobre el rango de fechas que elijas. Esta guía lista cada métrica que puede mostrar y la tabla que lee cada una, explica el trabajo diario detrás del conteo de usuarios activos, muestra cómo agregar una métrica propia y cubre el seguimiento opcional de visitantes que registra sesiones y eventos y los reenvía a la Conversions API de Facebook.

El dashboard

/admin es el componente Livewire App\Livewire\Admin\Dashboard. Tiene tres controles: Desde, Hasta y Métrica. El rango parte de los últimos 30 días, terminando hoy; un rango escrito al revés se intercambia, y una fecha que no se puede interpretar vuelve al valor por omisión. La métrica se guarda en la URL, así que /admin?metric=daily_revenue abre directo en esa gráfica y se puede guardar como marcador; la métrica por omisión, new_users, se deja fuera de la URL. Una métrica que la URL nombra pero el dashboard no ofrece vuelve a new_users.

Las barras o puntos de la gráfica se agrupan según la longitud del rango:

Rango Agrupación Etiqueta
Menos de 50 días Una por día 2026-09-03
Menos de 6 meses Una por semana, empezando en lunes La fecha del lunes
Menos de 3 años Una por mes 2026-09
Más largo Una por año 2026

Las métricas

Clave Etiqueta Gráfica Qué cuenta Se muestra cuando
new_users Nuevos Usuarios Registrados Columnas Usuarios por created_at. Siempre
total_users Usuarios Acumulados Totales Línea Usuarios existentes al final de cada periodo, partiendo del conteo anterior a Desde. Siempre
activity_log Eventos del registro de actividad Columnas Filas de activity_log por created_at. Siempre
logged_users Usuarios activos por día Columnas Las filas daily_logged_users de la tabla stats, sumadas por periodo. Mira más abajo. Siempre
users_by_country Usuarios por país Una columna por país Cada usuario agrupado por country_code, en mayúsculas; las cuentas sin país van en Desconocido. Ignora el rango de fechas. Siempre
tracking_events Seguimiento de Eventos Líneas: todos los eventos, más una por tipo de evento Filas de tracking_events por created_at. FEATURE_TRACKING_ENABLED=true
referral_subscriptions Suscripciones por referencias Dos series de columnas Referidos por registered_at, y recompensas de referido de tipo subscription por created_at. FEATURE_REFERRALS_ENABLED=true
daily_revenue Ingresos diarios (moneda) Columnas Eventos de webhook invoice.paid exitosos de Stripe que pertenecen a una suscripción y pagaron más de cero, en la moneda principal. El paquete de facturación está instalado
new_subscriptions Suscripciones creadas Líneas: todas las suscripciones pagadas, más una por plan Suscripciones cuyo precio es mayor que cero, por created_at. Los planes están activos
total_subscriptions Suscripciones Totales Acumuladas Líneas acumuladas, mismas series Las mismas suscripciones, acumuladas. Los planes están activos

"Los planes están activos" significa FEATURE_PLANS_ENABLED=true, un STRIPE_SECRET y al menos un plan en la base de datos. Ingresos diarios convierte cada factura de su moneda a pricing.primary_currency con el tipo de cambio guardado para ese día; una factura en una moneda sin tipo de cambio para su día cuenta como cero. Las filas de webhook que lee están descritas en /help/audit-trails-and-webhook-events, y los tipos de cambio en /help/currencies-intervals-and-exchange-rates.

Las gráficas las construye el trait App\Livewire\Traits\HasCharts sobre asantibanez/livewire-charts. Ofrece una gráfica de columnas, una de columnas con una columna por categoría, una de columnas con varias series, una de línea acumulada, una de varias líneas y una de varias líneas acumuladas, todas recibiendo el rango y el tamaño de agrupación y llenando con cero los periodos en los que nadie escribió. El paquete de facturación reutiliza el mismo trait, así que sus gráficas se ven como las del kit.

Usuarios activos por día

Es la única métrica que no se calcula desde una columna de fecha al cargar la página. La producen tres piezas:

  1. App\Http\Middleware\UpdateLastLoggedAt corre en cada petición web. En un GET de página completa de un usuario con sesión (no AJAX, no JSON, no una actualización de Livewire) llama a User::markLoggedIn(), que escribe users.last_logged_at una vez al día: sólo cuando la columna está vacía o guarda otra fecha. La escritura es silenciosa, así que no deja fila en el registro de actividad.
  2. php artisan stats:compute-daily (App\Console\Commands\ComputeDailyStats) cuenta los usuarios cuyo last_logged_at cae en ayer e inserta o actualiza una fila en stats: clave daily_logged_users, date ayer, value el conteo. La tabla tiene un índice único sobre clave y fecha, así que correrlo dos veces para el mismo día actualiza la fila.
  3. routes/console.php programa ese comando con dailyAt('00:00'), en la zona horaria de la aplicación. Sólo corre cuando corre el scheduler, así que el servidor necesita php artisan schedule:run cada minuto en su cron. Mira /help/queues-and-scheduled-work.

Un día que el scheduler se saltó aparece en cero, y el comando siempre cuenta ayer: no se le puede apuntar a una fecha anterior. La migración 2026_05_05_000001_backfill_daily_logged_users_stats llena el historial una vez, desde el registro de actividad: por cada día, el número de usuarios distintos que causaron una fila de actividad, saltando los días que ya tienen estadística.

Agrega una métrica propia

Todo vive en app/Livewire/Admin/Dashboard.php. El ejemplo cuenta facturas abiertas; cámbialo por tu modelo.

  1. Ofrécela en el selector:
#[Computed]
public function metricOptions()
{
    $options = [
        'new_users' => __('New Registered Users'),
        // ...
        'open_invoices' => __('Open Invoices'),
    ];
  1. Dirígela a un constructor en chartModel():
return match ($this->metric) {
    'new_users' => $this->newUsersChart($from, $to, $granularity),
    // ...
    'open_invoices' => $this->openInvoicesChart($from, $to, $granularity),
    default => $this->newUsersChart($from, $to, $granularity),
};
  1. Escribe el constructor. Agrupa las filas con chartBucketKey() para que caigan en los mismos periodos que dibuja la gráfica, y entrega la colección a uno de los constructores del trait:
private function openInvoicesChart(Carbon $from, Carbon $to, string $granularity)
{
    $data = Invoice::where('status', 'open')
        ->whereBetween('created_at', [$from, $to])
        ->get(['created_at'])
        ->groupBy(function ($invoice) use ($granularity) {
            return $this->chartBucketKey($invoice->created_at, $granularity);
        })
        ->map(function ($invoices) {
            return $invoices->count();
        });

    return $this->buildColumnChart($from, $to, $data, __('Open Invoices'), $granularity);
}
  1. Si es una gráfica de línea, agrega su clave a isLineChart(); la vista elige el componente de línea o el de columnas a partir de eso.

La etiqueta pasa por __(), así que agrégala a lang/en.json y lang/es.json. Mira /help/languages-and-translations. Envuelve la opción en una condición cuando la métrica pertenezca a una bandera, como lo hacen las métricas de seguimiento y de referidos.

Seguimiento de visitantes

El seguimiento viene apagado. Es una sola bandera:

Variable de entorno Valor por omisión Qué se activa
FEATURE_TRACKING_ENABLED false Se abren sesiones para los visitantes, trackingEvent() registra eventos, aparecen las tres pantallas de Tracking en el panel de administración y Seguimiento de Eventos aparece en el dashboard.

La variable no está en .env.example; agrega la línea tú mismo. Con la bandera apagada, el middleware deja pasar las peticiones, el helper regresa sin hacer nada y las políticas de seguimiento niegan todas las pantallas.

Sesiones

App\Http\Middleware\TrackingMiddleware corre en cada petición web. En un GET de página completa (no AJAX, no JSON, no una actualización de Livewire) resuelve una sesión de seguimiento cuando el visitante todavía no tiene una, o de nuevo cuando la URL trae fbclid o utm_campaign, para que un regreso por un enlace de campaña nuevo quede atribuido. TrackingService busca la sesión en este orden: el id guardado en la sesión de Laravel bajo new_tracking_session_id, la cookie del mismo nombre, y luego cualquier sesión con la misma dirección IP; si las tres fallan, crea una. El id se escribe en la sesión, y en la cookie por 365 días cuando la sesión es nueva.

Una fila de sesión (tracking_sessions) guarda ip_address, user_agent, fbclid, fbp (la cookie _fbp), fbc (la cookie _fbc, o construida desde fbclid), referrer_url, current_url, campaign (utm_campaign) y user_id. Los campos de atribución — fbp, fbc, fbclid, referrer_url, campaign — se escriben la primera vez que llegan y nunca se sobrescriben. user_id se llena cuando se ve a un visitante con sesión iniciada y la sesión no tiene uno.

Eventos

Dispara un evento desde cualquier lugar de la aplicación con el helper global:

trackingEvent('Checkout Started', ['plan' => 'pro', 'amount' => 29]);

El helper no hace nada cuando la bandera está apagada. Si no, toma la sesión actual (el id en la sesión de Laravel, o la primera sesión del usuario con sesión iniciada) y registra el evento en ella; sin sesión — un job en cola, un comando — no se registra nada. Una clave user_id en el payload asocia a ese usuario con la sesión. El nombre del evento es un TrackingEventType, creado en el primer uso con el nombre como título. Una sesión registra cada tipo de evento una sola vez: un segundo Register Started en la misma sesión se ignora.

El kit dispara cuatro eventos:

Evento Cuándo Payload
Register Started Se abre la página de registro.
Register Completed Se crea un usuario. user_id
Subscribed Un usuario toma un plan o lo cambia. user_id, variation_id
Subscription Paid Stripe confirma una factura de suscripción. Se registra en la sesión del usuario, creando una cuando el usuario no tiene ninguna. invoice_id, subscription_id, amount, currency

Reenvío a Facebook

App\Observers\TrackingEventObserver envía cada evento nuevo a la Conversions API de Facebook, dentro de la misma petición que lo registró, a través de TrackingService::sendToFacebook():

Variable de entorno Clave de configuración Qué hace
FACEBOOK_ACCESS_TOKEN services.facebook.access_token El token de usuario del sistema de la Conversions API.
FACEBOOK_PIXEL_ID services.facebook.pixel_id El píxel al que se publican los eventos, en graph.facebook.com/v18.0/{pixel}/events.
FACEBOOK_TEST_EVENT_CODE services.facebook.test_event_code Opcional. Cuando está definido, cada envío lleva test_event_code, así que los eventos aparecen en la pestaña Test Events del Events Manager en lugar de en producción.

Sin el token y el id del píxel, los eventos se guardan y no se envía nada. Cada envío lleva el nombre del tipo de evento como event_name, la hora, las URL actual y de referencia de la sesión, action_source website, el payload como custom_data, y user_data con fbc, fbp, IP y user agent, más el SHA-256 del correo y el nombre del usuario en minúsculas cuando la sesión tiene usuario. Un envío fallido se escribe en el log como advertencia y nunca rompe la petición.

Las pantallas de administración

Tres recursos bajo Tracking en la barra lateral, todos detrás de la bandera:

URL Qué muestra
/admin/tracking-event-types Los nombres de evento, con un título y una descripción que puedes editar. El dashboard y la lista de eventos muestran el título, así que renombra aquí sin tocar el código que dispara el evento. Un tipo creado sin nombre toma su título como nombre.
/admin/tracking-sessions Sólo índice y detalle: usuario, número de eventos, IP, campaña, identificadores de Facebook, URL, user agent y los eventos de la sesión.
/admin/tracking-events Cada evento con su tipo, sesión, usuario y payload. Filtra por tipo, por rango de fechas y por id de sesión.

Leerlas requiere los permisos retrieve tracking_event_type, retrieve tracking_session y retrieve tracking_event. Mira /help/roles-and-permissions.