Click Stats Dashboard Widget

  • JNext lesson
  • KPrevious lesson
  • FSearch lessons
  • EscClear search

Add a WordPress admin dashboard widget that displays click statistics at a glance. Shows total clicks across multiple time ranges, a visual sparkline chart of daily clicks, your top 10 most-clicked links, and top referrers — all without leaving the WordPress dashboard.

Requirements

  • GT Link Manager 1.5+ installed and active
  • The Click Log: Custom Database Table snippet must be installed first (creates the wp_gtlm_click_log table)
  • User must have the edit_posts capability (filterable via gtlm_capabilities)

Installation

  • Install the Click Log: Custom Database Table snippet first if you haven’t already
  • Add the snippet below to your theme’s functions.php or a site-specific plugin
  • Visit your WordPress Dashboard — the widget appears automatically

The Code

<?php
/**
 * GT Link Manager - Click Stats Dashboard Widget
 *
 * Adds a WordPress admin dashboard widget showing:
 * - Total clicks (today, 7 days, 30 days, all time)
 * - Top 10 clicked links (last 30 days)
 * - Clicks-per-day sparkline chart (last 30 days)
 * - Top referrers
 *
 * Requires: click-log-db.php snippet (creates the wp_gtlm_click_log table).
 */

defined( 'ABSPATH' ) || exit;

add_action( 'wp_dashboard_setup', 'gtlm_stats_register_widget' );

function gtlm_stats_register_widget(): void {
	if ( ! current_user_can( apply_filters( 'gtlm_capabilities', 'edit_posts', 'dashboard_widget' ) ) ) {
		return;
	}

	wp_add_dashboard_widget(
		'gtlm_click_stats',
		'GT Link Manager - Click Stats',
		'gtlm_stats_render_widget'
	);
}

function gtlm_stats_render_widget(): void {
	global $wpdb;

	$table = $wpdb->prefix . 'gtlm_click_log';

	// Check if click log table exists.
	$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table ) );
	if ( ! $table_exists ) {
		echo '<p>No click data yet. Install the <strong>click-log-db.php</strong> snippet to start logging clicks.</p>';
		return;
	}

	$now = current_time( 'mysql', true );

	// ── Summary counts ───────────────────────────────────────────────────────
	$today   = gmdate( 'Y-m-d 00:00:00' );
	$week    = gmdate( 'Y-m-d 00:00:00', strtotime( '-7 days' ) );
	$month   = gmdate( 'Y-m-d 00:00:00', strtotime( '-30 days' ) );

	// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	$count_today = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $today ) );
	$count_week  = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $week ) );
	$count_month = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE created_at >= %s", $month ) );
	$count_all   = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table}" );

	echo '<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px;text-align:center;">';
	printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">Today</div></div>', number_format_i18n( $count_today ) );
	printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">7 Days</div></div>', number_format_i18n( $count_week ) );
	printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">30 Days</div></div>', number_format_i18n( $count_month ) );
	printf( '<div><div style="font-size:24px;font-weight:700;color:#1d2327;">%s</div><div style="color:#646970;font-size:12px;">All Time</div></div>', number_format_i18n( $count_all ) );
	echo '</div>';

	// ── Daily clicks chart (last 30 days) ────────────────────────────────────
	$daily = $wpdb->get_results( $wpdb->prepare(
		"SELECT DATE(created_at) as day, COUNT(*) as clicks FROM {$table} WHERE created_at >= %s GROUP BY DATE(created_at) ORDER BY day ASC",
		$month
	) );

	if ( $daily ) {
		$max_clicks = max( array_column( $daily, 'clicks' ) );
		$days_map   = [];
		foreach ( $daily as $row ) {
			$days_map[ $row->day ] = (int) $row->clicks;
		}

		echo '<div style="margin-bottom:20px;">';
		echo '<h4 style="margin:0 0 8px;font-size:13px;color:#646970;">Clicks per Day (30 days)</h4>';
		echo '<div style="display:flex;align-items:end;gap:2px;height:60px;">';

		for ( $i = 29; $i >= 0; $i-- ) {
			$day    = gmdate( 'Y-m-d', strtotime( "-{$i} days" ) );
			$clicks = $days_map[ $day ] ?? 0;
			$height = $max_clicks > 0 ? max( 2, round( ( $clicks / $max_clicks ) * 60 ) ) : 2;
			$color  = $clicks > 0 ? '#2271b1' : '#dcdcde';
			printf(
				'<div title="%s: %s clicks" style="flex:1;height:%dpx;background:%s;border-radius:2px 2px 0 0;min-width:4px;"></div>',
				esc_attr( $day ),
				number_format_i18n( $clicks ),
				$height,
				$color
			);
		}

		echo '</div></div>';
	}

	// ── Top 10 links ─────────────────────────────────────────────────────────
	$top_links = $wpdb->get_results( $wpdb->prepare(
		"SELECT slug, COUNT(*) as clicks FROM {$table} WHERE created_at >= %s GROUP BY slug ORDER BY clicks DESC LIMIT 10",
		$month
	) );

	if ( $top_links ) {
		echo '<h4 style="margin:0 0 8px;font-size:13px;color:#646970;">Top Links (30 days)</h4>';
		echo '<table style="width:100%;border-collapse:collapse;font-size:13px;">';

		$top_max = (int) $top_links[0]->clicks;
		foreach ( $top_links as $i => $row ) {
			$bar_width = $top_max > 0 ? round( ( (int) $row->clicks / $top_max ) * 100 ) : 0;
			$bg        = $i % 2 === 0 ? '#f6f7f7' : '#fff';
			printf(
				'<tr style="background:%s;"><td style="padding:4px 8px;"><code>%s</code></td><td style="padding:4px 8px;text-align:right;white-space:nowrap;">%s</td><td style="padding:4px 8px;width:40%%;"><div style="height:8px;background:#2271b1;border-radius:4px;width:%d%%;"></div></td></tr>',
				$bg,
				esc_html( $row->slug ),
				number_format_i18n( (int) $row->clicks ),
				$bar_width
			);
		}

		echo '</table>';
	}

	// ── Top referrers ────────────────────────────────────────────────────────
	$referrers = $wpdb->get_results( $wpdb->prepare(
		"SELECT referrer, COUNT(*) as clicks FROM {$table} WHERE created_at >= %s AND referrer != '' GROUP BY referrer ORDER BY clicks DESC LIMIT 5",
		$month
	) );

	if ( $referrers ) {
		echo '<h4 style="margin:16px 0 8px;font-size:13px;color:#646970;">Top Referrers (30 days)</h4>';
		echo '<ol style="margin:0;padding-left:20px;font-size:13px;">';
		foreach ( $referrers as $row ) {
			$host = wp_parse_url( $row->referrer, PHP_URL_HOST ) ?: $row->referrer;
			printf(
				'<li style="margin-bottom:4px;">%s <span style="color:#646970;">(%s)</span></li>',
				esc_html( $host ),
				number_format_i18n( (int) $row->clicks )
			);
		}
		echo '</ol>';
	}
	// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}

How It Works

  • Registers a dashboard widget via wp_dashboard_setup with permission checking
  • Displays four summary counters: Today, 7 Days, 30 Days, and All Time click totals
  • Renders a 30-day sparkline bar chart showing daily click volume using inline CSS (no external JS libraries needed)
  • Lists the top 10 most-clicked link slugs with click counts and visual bar indicators
  • Shows the top 5 referrer domains with click counts
  • Gracefully handles missing click log table with an informative message

Configuration Notes

The widget respects the gtlm_capabilities filter, so you can control which user roles see the stats. By default, any user with edit_posts can view the widget.

All queries use WordPress’s $wpdb with prepared statements for security. The widget is rendered with inline styles for compatibility across WordPress admin themes.