# learnpress/4.4.5/assets/src/js/admin/statistics/tab-overview.js

LearnPress – WordPress LMS Plugin for Create and Sell Online Courses, version 4.4.5. 336 lines.

- Page: https://pluginprobe.com/plugins/learnpress/4.4.5/code/assets/src/js/admin/statistics/tab-overview.js
- Raw: https://pluginprobe.com/plugins/learnpress/4.4.5/raw/assets/src/js/admin/statistics/tab-overview.js
- Modified: 2026-08-22T04:23:06+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/learnpress/4.4.5/code/assets/src/js/admin/statistics/tab-overview.js#L10-L20`.

```javascript
/**
 * Overview tab module — fetches the `dashboard` payload and renders
 * KPIs, dual-line chart, funnel, tables, order health and health checks.
 *
 * Listens to lp-stats:filter-changed; never mutates state itself.
 *
 * @since 4.4.2
 * @version 1.0.0
 */

import * as lpUtils from 'lpAssetsJsPath/utils.js';
import { LP_STATS_FILTER_CHANGED, LP_STATS_EXPORT_CSV } from './state.js';
import { lpStatsFetch, getStatsConfig, getStatsI18n } from './api.js';
import { renderKpi } from './kpi.js';
import { renderLineChart } from './chart.js';
import { renderDataTable } from './data-table.js';
import { lpStatsReportModal } from './report-modal.js';
import { exportCsv, buildCsvFilename } from './csv.js';

const sprintfLite = ( template, value ) =>
	String( template ).replace( /%[ds]/, String( value ) ).replace( /%%/g, '%' );

export class LpStatsTabOverview {
	static selectors = {
		elContainer: '.lp-stats-tab-overview',
		elChartCanvas: '#net-sales-chart-content',
		elFunnelStep: '.lp-stats-funnel__step',
		elTableTopCourses: '.lp-stats-table-top-courses',
		elTableInstructors: '.lp-stats-table-instructors',
		elBtnViewAllCourses: '.lp-stats-view-all-courses',
		elOrderHealthBox: '.lp-stats-order-health .lp-stats-health-box',
		elHealthCheckCount: '.lp-stats-health-check__count',
		elSkeleton: '.lp-skeleton-animation',
	};

	static kpiCards = {
		net_sales: '.lp-kpi-net-sales',
		completed_orders: '.lp-kpi-completed-orders',
		enrollments: '.lp-kpi-enrollments',
		completion_rate: '.lp-kpi-completion-rate',
		active_learners: '.lp-kpi-active-learners',
		failed_orders: '.lp-kpi-failed-orders',
	};

	constructor() {
		this.elContainer = null;
		this.isRequesting = false;
		this.pendingReload = false;
		this.tables = {};
	}

	init() {
		this.elContainer = document.querySelector(
			LpStatsTabOverview.selectors.elContainer
		);
		if ( ! this.elContainer ) {
			return;
		}

		this.events();
		this.loadData();
	}

	events() {
		if ( LpStatsTabOverview._loadedEvents ) {
			return;
		}
		LpStatsTabOverview._loadedEvents = this;

		lpUtils.eventHandlers( 'click', [
			{
				selector: LpStatsTabOverview.selectors.elBtnViewAllCourses,
				class: this,
				callBack: this.viewAllCourses.name,
			},
		] );

		document.addEventListener( LP_STATS_FILTER_CHANGED, () => this.loadData() );
		document.addEventListener( LP_STATS_EXPORT_CSV, () => this.exportTables() );
	}

	toggleSkeletons( show ) {
		this.elContainer
			.querySelectorAll( LpStatsTabOverview.selectors.elSkeleton )
			.forEach( ( el ) => {
				el.style.display = show ? 'block' : 'none';
			} );
	}

	loadData() {
		if ( this.isRequesting ) {
			// Latest filter wins: re-run once the in-flight request finishes.
			this.pendingReload = true;
			return;
		}
		this.isRequesting = true;

		lpStatsFetch(
			'overviews-statistics',
			{},
			{
				before: () => this.toggleSkeletons( true ),
				success: ( response ) => this.render( response.data?.dashboard ),
				error: ( err ) => {
					console.error( 'LP Statistics overview:', err );
					this.render( null );
				},
				completed: () => {
					this.toggleSkeletons( false );
					this.isRequesting = false;
					if ( this.pendingReload ) {
						this.pendingReload = false;
						this.loadData();
					}
				},
			}
		);
	}

	/**
	 * @param {Object|null} dashboard `dashboard` key of the response; null/missing
	 *                                renders empty states, never throws.
	 */
	render( dashboard ) {
		if ( ! dashboard ) {
			console.error( 'LP Statistics overview: dashboard payload missing.' );
			dashboard = {};
		}

		this.renderKpis( dashboard.kpis || {} );
		this.renderChart( dashboard.chart || {} );
		this.renderFunnel( dashboard.funnel || {} );
		this.renderTables( dashboard );
		this.renderOrderHealth( dashboard.order_health || {} );
		this.renderHealthChecks( dashboard.health_checks || {} );
	}

	renderKpis( kpis ) {
		const i18n = ( key, fallback ) => getStatsI18n( key, fallback );

		Object.entries( LpStatsTabOverview.kpiCards ).forEach(
			( [ key, selector ] ) => {
				const elCard = this.elContainer.querySelector( selector );
				const payload = { ...( kpis[ key ] || {} ) };

				if ( 'completed_orders' === key && payload.aov_formatted ) {
					payload.subline = sprintfLite(
						i18n( 'aov', 'Avg. order value: %s' ),
						payload.aov_formatted
					);
				}
				if ( 'completion_rate' === key ) {
					if ( 'number' === typeof payload.value ) {
						payload.formatted = `${ payload.value }%`;
					}
					payload.subline = sprintfLite(
						i18n( 'belowTarget', '%d below completion target' ),
						payload.courses_below_target ?? 0
					);
				}
				if ( 'failed_orders' === key && null != payload.fail_rate_pct ) {
					payload.subline = sprintfLite(
						i18n( 'failRate', '%s%% of all orders' ),
						payload.fail_rate_pct
					);
				}

				renderKpi( elCard, payload );
			}
		);
	}

	renderChart( chart ) {
		renderLineChart( LpStatsTabOverview.selectors.elChartCanvas, {
			labels: chart.labels || [],
			datasets: [
				{
					label: getStatsI18n( 'revenue', 'Revenue' ),
					data: chart.revenue || [],
					yAxisID: 'y',
				},
				{
					label: getStatsI18n( 'enrollments', 'Enrollments' ),
					data: chart.enrollments || [],
					yAxisID: 'y1',
				},
			],
			xLabel: chart.x_label || '',
			granularity: chart.granularity || '',
		} );
	}

	renderFunnel( funnel ) {
		const steps = [ 'registered', 'enrolled', 'started', 'completed' ];
		let previous = null;

		steps.forEach( ( step ) => {
			const elStep = this.elContainer.querySelector(
				`${ LpStatsTabOverview.selectors.elFunnelStep }[data-step="${ step }"]`
			);
			if ( ! elStep ) {
				return;
			}

			const count = Number( funnel[ step ] ?? 0 );
			const base = null === previous ? count : previous;
			const width = base > 0 ? Math.min( 100, ( count / base ) * 100 ) : 0;

			elStep.querySelector( '.lp-stats-funnel__count' ).textContent =
				String( count );
			elStep.querySelector( '.lp-stats-funnel__bar' ).style.width = `${ width }%`;
			previous = count;
		} );
	}

	completionBadge( rate ) {
		if ( null == rate ) {
			return '';
		}
		const { completionBadge = {} } = getStatsConfig();
		const green = completionBadge.green ?? 60;
		const yellow = completionBadge.yellow ?? 40;

		if ( rate >= green ) {
			return 'green';
		}
		return rate >= yellow ? 'yellow' : 'red';
	}

	topCoursesColumns() {
		return [
			{ key: 'course_name', label: getStatsI18n( 'course', 'Course' ) },
			{
				key: 'revenue_formatted',
				label: getStatsI18n( 'revenue', 'Revenue' ),
				csv: ( row ) => row.revenue,
			},
			{ key: 'order_count', label: getStatsI18n( 'orders', 'Orders' ) },
			{ key: 'enrolled', label: getStatsI18n( 'enrolled', 'Enrolled' ) },
			{
				key: 'completion_rate',
				label: getStatsI18n( 'completion', 'Completion' ),
				format: ( value ) => ( null == value ? '–' : `${ value }%` ),
				badge: ( row ) => this.completionBadge( row.completion_rate ),
			},
		];
	}

	instructorColumns() {
		return [
			{ key: 'instructor_name', label: getStatsI18n( 'instructor', 'Instructor' ) },
			{ key: 'course_count', label: getStatsI18n( 'courses', 'Courses' ) },
			{
				key: 'revenue_formatted',
				label: getStatsI18n( 'revenue', 'Revenue' ),
				csv: ( row ) => row.revenue,
			},
			{ key: 'enrolled', label: getStatsI18n( 'enrolled', 'Enrolled' ) },
			{
				key: 'completion_rate',
				label: getStatsI18n( 'completion', 'Completion' ),
				format: ( value ) => ( null == value ? '–' : `${ value }%` ),
				badge: ( row ) => this.completionBadge( row.completion_rate ),
			},
		];
	}

	renderTables( dashboard ) {
		this.tables[ 'top-courses' ] = renderDataTable(
			this.elContainer.querySelector(
				LpStatsTabOverview.selectors.elTableTopCourses
			),
			this.topCoursesColumns(),
			dashboard.top_courses || []
		);

		this.tables.instructors = renderDataTable(
			this.elContainer.querySelector(
				LpStatsTabOverview.selectors.elTableInstructors
			),
			this.instructorColumns(),
			dashboard.instructor_summary || []
		);
	}

	renderOrderHealth( orderHealth ) {
		this.elContainer
			.querySelectorAll( LpStatsTabOverview.selectors.elOrderHealthBox )
			.forEach( ( elBox ) => {
				const status = elBox.dataset.status;
				const elCount = elBox.querySelector( '.lp-stats-health-box__count' );
				if ( elCount ) {
					elCount.textContent = String( orderHealth[ status ] ?? 0 );
				}
			} );
	}

	renderHealthChecks( healthChecks ) {
		this.elContainer
			.querySelectorAll( LpStatsTabOverview.selectors.elHealthCheckCount )
			.forEach( ( elCount ) => {
				const check = elCount.dataset.check;
				elCount.textContent = String( healthChecks[ check ] ?? 0 );
			} );
	}

	viewAllCourses( args ) {
		const btn = args.target.closest(
			LpStatsTabOverview.selectors.elBtnViewAllCourses
		);
		if ( ! btn || ! this.elContainer.contains( btn ) ) {
			return;
		}

		lpStatsReportModal.open( {
			report: 'top_courses',
			title: getStatsI18n( 'topCourses', 'Top courses' ),
			tableId: 'top-courses',
		} );
	}

	exportTables() {
		Object.entries( this.tables ).forEach( ( [ tableId, handle ] ) => {
			if ( handle && handle.rows.length ) {
				exportCsv(
					buildCsvFilename( 'overview', tableId ),
					handle.columns,
					handle.rows
				);
			}
		} );
	}
}

export const lpStatsTabOverview = new LpStatsTabOverview();

```
