# learnpress/4.4.3/assets/src/js/admin/statistics/tab-instructors.js

LearnPress – WordPress LMS Plugin for Create and Sell Online Courses, version 4.4.3. 402 lines.

- Page: https://pluginprobe.com/plugins/learnpress/4.4.3/code/assets/src/js/admin/statistics/tab-instructors.js
- Raw: https://pluginprobe.com/plugins/learnpress/4.4.3/raw/assets/src/js/admin/statistics/tab-instructors.js
- Modified: 2026-07-26T12:32:48+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.3/code/assets/src/js/admin/statistics/tab-instructors.js#L10-L20`.

```javascript
/**
 * Instructors tab module.
 *
 * Fetches the `dashboard` payload and renders KPIs, the operations widget,
 * instructor performance + course watchlist tables, per-instructor report
 * popup, and CSV export.
 *
 * @since 4.4.2
 * @version 1.0.0
 */

import * as lpUtils from 'lpAssetsJsPath/utils.js';
import { lpStatsState, 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 LpStatsTabInstructors {
	static selectors = {
		elContainer: '.lp-stats-tab-instructors',
		elChartCanvas: '#instructor-chart-content',
		elTablePerformance: '.lp-stats-table-instructor-performance',
		elTableWatchlist: '.lp-stats-table-instructor-watchlist',
		elBtnViewAllInstructors: '.lp-stats-view-all-instructors',
		elPerformanceRow: '.lp-stats-instructor-performance-row',
		elOperationsRow: '.lp-stats-operations__row',
		elSkeleton: '.lp-skeleton-animation',
	};

	static kpiCards = {
		active_instructors: '.lp-kpi-active-instructors',
		instructor_revenue: '.lp-kpi-instructor-revenue',
		courses_managed: '.lp-kpi-courses-managed',
		students_reached: '.lp-kpi-students-reached',
		avg_completion: '.lp-kpi-avg-completion',
		needs_review: '.lp-kpi-needs-review',
	};

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

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

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

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

		lpUtils.eventHandlers( 'click', [
			{
				selector: LpStatsTabInstructors.selectors.elBtnViewAllInstructors,
				class: this,
				callBack: this.viewAllInstructors.name,
			},
			{
				selector: LpStatsTabInstructors.selectors.elPerformanceRow,
				class: this,
				callBack: this.openInstructorReport.name,
			},
		] );

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

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

	loadData() {
		if ( this.isRequesting ) {
			this.pendingReload = true;
			return;
		}
		this.isRequesting = true;

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

	render( data ) {
		if ( ! data?.dashboard ) {
			console.error( 'LP Statistics instructors: dashboard payload missing.' );
			data = { chart_data: {}, dashboard: {} };
		}

		const dashboard = data.dashboard || {};

		this.renderKpis( dashboard.kpis || {} );
		this.renderChart( data.chart_data || {} );
		this.renderOperations( dashboard.operations || {} );
		this.renderTables( dashboard );
	}

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

				if ( 'active_instructors' === key ) {
					payload.subline = sprintfLite(
						getStatsI18n( 'ofTotalInstructors', '%d total' ),
						payload.total ?? 0
					);
				}
				if ( 'instructor_revenue' === key && null != payload.contribution_pct ) {
					payload.subline = sprintfLite(
						getStatsI18n( 'ofNetSales', '%s%% of net sales' ),
						payload.contribution_pct
					);
				}
				if ( 'avg_completion' === key && 'number' === typeof payload.value ) {
					payload.formatted = `${ payload.value }%`;
				}

				renderKpi( elCard, payload );
			}
		);
	}

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

	renderOperations( operations ) {
		this.elContainer
			.querySelectorAll( LpStatsTabInstructors.selectors.elOperationsRow )
			.forEach( ( elRow ) => {
				const op = elRow.dataset.op;
				const elValue = elRow.querySelector( '.lp-stats-operations__value' );
				const elName = elRow.querySelector( '.lp-stats-operations__name' );
				const data = operations[ op ];

				if ( elName ) {
					elName.textContent = '';
				}

				if ( null == data ) {
					if ( elValue ) {
						elValue.textContent = '–';
					}
					return;
				}

				// Scalar operations (counts) vs highlight objects ({ name, value }).
				if ( 'object' === typeof data ) {
					if ( elValue ) {
						elValue.textContent =
							data.value_formatted ??
							( 'number' === typeof data.value && op === 'top_completion'
								? `${ data.value }%`
								: String( data.value ?? '' ) );
					}
					if ( elName ) {
						elName.textContent = data.name || '';
					}
				} else if ( elValue ) {
					elValue.textContent = String( data );
				}
			} );
	}

	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';
	}

	riskLabel( slug ) {
		const labels = {
			high: getStatsI18n( 'riskHigh', 'High' ),
			medium: getStatsI18n( 'riskMedium', 'Medium' ),
			healthy: getStatsI18n( 'riskHealthy', 'Healthy' ),
		};

		return labels[ slug ] || String( slug || '' );
	}

	actionLabel( slug ) {
		const { watchlistActions = {} } = getStatsConfig().i18n || {};
		return watchlistActions[ slug ] || String( slug || '' );
	}

	performanceColumns() {
		return [
			{ key: 'name', label: getStatsI18n( 'instructor', 'Instructor' ) },
			{ key: 'courses', label: getStatsI18n( 'courses', 'Courses' ) },
			{ key: 'students', label: getStatsI18n( 'students', 'Students' ) },
			{
				key: 'revenue_formatted',
				label: getStatsI18n( 'revenue', 'Revenue' ),
				csv: ( row ) => row.revenue,
			},
			{
				key: 'avg_completion',
				label: getStatsI18n( 'completion', 'Completion' ),
				format: ( value ) => ( null == value ? '–' : `${ value }%` ),
				badge: ( row ) => this.completionBadge( row.avg_completion ),
			},
		];
	}

	watchlistColumns() {
		return [
			{ key: 'name', label: getStatsI18n( 'course', 'Course' ) },
			{ key: 'instructor', label: getStatsI18n( 'instructor', 'Instructor' ) },
			{
				key: 'completion_rate',
				label: getStatsI18n( 'completion', 'Completion' ),
				format: ( value ) => ( null == value ? '–' : `${ value }%` ),
			},
			{
				key: 'risk',
				label: getStatsI18n( 'risk', 'Risk' ),
				// Emoji lives in CSS pseudo-content on .lp-risk--{slug}; text stays clean.
				format: ( value ) => {
					const span = document.createElement( 'span' );
					span.className = `lp-badge lp-risk lp-risk--${ value }`;
					span.textContent = this.riskLabel( value );
					return span;
				},
				csv: ( row ) => this.riskLabel( row.risk ),
			},
			{
				key: 'action',
				label: getStatsI18n( 'actionRequired', 'Action required' ),
				format: ( value ) => this.actionLabel( value ),
				csv: ( row ) => this.actionLabel( row.action ),
			},
		];
	}

	renderTables( dashboard ) {
		const performanceRows = dashboard.performance || [];
		this.tables.performance = renderDataTable(
			this.elContainer.querySelector(
				LpStatsTabInstructors.selectors.elTablePerformance
			),
			this.performanceColumns(),
			performanceRows,
			{ emptyText: getStatsI18n( 'noInstructorData', 'No instructor data in this period.' ) }
		);
		this.decoratePerformanceRows( performanceRows );

		this.tables.watchlist = renderDataTable(
			this.elContainer.querySelector(
				LpStatsTabInstructors.selectors.elTableWatchlist
			),
			this.watchlistColumns(),
			dashboard.watchlist || []
		);
	}

	decoratePerformanceRows( rows = [] ) {
		const scopedInstructor = lpStatsState.get().instructor_id;
		const tableRows = this.elContainer.querySelectorAll(
			`${ LpStatsTabInstructors.selectors.elTablePerformance } tbody tr`
		);

		rows.forEach( ( row, index ) => {
			const tableRow = tableRows[ index ];
			if ( ! tableRow || ! row.instructor_id ) {
				return;
			}

			tableRow.classList.add( 'lp-stats-instructor-performance-row' );
			tableRow.dataset.instructorId = String( row.instructor_id );
			tableRow.dataset.instructorName = row.name || '';

			if ( scopedInstructor && scopedInstructor === row.instructor_id ) {
				tableRow.classList.add( 'is-highlighted' );
			}
		} );
	}

	openInstructorReport( args ) {
		const row = args.target.closest(
			LpStatsTabInstructors.selectors.elPerformanceRow
		);
		if ( ! row || ! this.elContainer.contains( row ) ) {
			return;
		}

		const instructorId = parseInt( row.dataset.instructorId, 10 ) || 0;
		if ( instructorId <= 0 ) {
			return;
		}
		const instructorName =
			row.dataset.instructorName || getStatsI18n( 'instructorReport', 'Instructor report' );

		lpStatsReportModal.open( {
			report: 'instructor_report',
			title: instructorName,
			tableId: `instructor-${ instructorId }`,
			args: { instructor_id: instructorId },
		} );
	}

	viewAllInstructors( args ) {
		const btn = args.target.closest(
			LpStatsTabInstructors.selectors.elBtnViewAllInstructors
		);
		if ( ! btn || ! this.elContainer.contains( btn ) ) {
			return;
		}

		lpStatsReportModal.open( {
			report: 'instructor_performance',
			title: getStatsI18n(
				'instructorPerformance',
				'Instructor performance'
			),
			tableId: 'instructor-performance',
		} );
	}

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

export const lpStatsTabInstructors = new LpStatsTabInstructors();

```
