PluginProbe ʕ •ᴥ•ʔ
Tutor LMS – eLearning and online course solution / 4.0.1
Tutor LMS – eLearning and online course solution v4.0.1
4.0.1 4.0.0 3.9.15 3.9.14 3.9.13 3.9.12 3.9.11 trunk 1.0.0 1.0.0-alpha 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.11 1.2.12 1.2.13 1.2.20 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7 1.7.8 1.7.9 1.8.0 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9.0 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.14 1.9.15 1.9.16 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1.0 2.1.1 2.1.10 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.3.0 2.4.0 2.5.0 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 3.0.0 3.0.1 3.0.2 3.1.0 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.6.1 3.6.2 3.6.3 3.6.4 3.7.0 3.7.1 3.7.2 3.7.3 3.7.4 3.8.0 3.8.1 3.8.2 3.8.3 3.9.0 3.9.1 3.9.10 3.9.2 3.9.3 3.9.4 3.9.5 3.9.6 3.9.7 3.9.8 3.9.9
tutor / templates / dashboard / instructor / home.php
tutor / templates / dashboard / instructor Last commit date
analytics 5 days ago home 5 days ago apply_for_instructor.php 5 days ago dashboard-empty.php 5 days ago home.php 5 days ago instructor-request-alert.php 5 days ago logged-in.php 5 days ago profile-statistics.php 5 days ago registration.php 5 days ago
home.php
560 lines
1 <?php
2 /**
3 * Dashboard page: Home for Instructor.
4 *
5 * @package Tutor\Templates
6 * @author Themeum <support@themeum.com>
7 * @link https://themeum.com
8 * @since 4.0.0
9 */
10
11 defined( 'ABSPATH' ) || exit;
12
13 use TUTOR\Icon;
14 use TUTOR\Input;
15 use TUTOR\Instructor;
16 use TUTOR_REPORT\Analytics;
17 use Tutor\Models\CourseModel;
18 use Tutor\Models\WithdrawModel;
19 use Tutor\Components\DateFilter;
20 use Tutor\Components\InputField;
21 use Tutor\Components\Constants\InputType;
22 use Tutor\Components\SvgIcon;
23
24 $upcoming_tasks = array();
25 $get_upcoming_live_tasks = array();
26 $overview_chart_data = array();
27 $recent_reviews = array();
28 $course_completion_data = array();
29 $sortable_sections = array();
30
31 $user = wp_get_current_user();
32 $instructor_course_ids = CourseModel::get_courses_by_args(
33 array(
34 'post_author' => $user->ID,
35 'posts_per_page' => -1,
36 'fields' => 'ids',
37 )
38 )->posts;
39
40 $tutor_pro_enabled = tutor_utils()->is_plugin_active( 'tutor-pro/tutor-pro.php' );
41 $is_pro_reports = $tutor_pro_enabled && tutor_utils()->is_addon_enabled( 'tutor-report' );
42
43 $start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : '';
44 $end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : '';
45 $is_all_time = empty( $start_date ) && empty( $end_date );
46 $previous_dates = $is_all_time ? array() : Instructor::get_comparison_date_range( $start_date, $end_date );
47
48 $date_range = fn( $from, $to ): array => array(
49 'from' => $from,
50 'to' => $to,
51 );
52
53 $stat = function ( $current, $previous, $previous_dates ) {
54 return array_merge( $previous_dates, Instructor::get_stat_card_details( (float) $current, (float) $previous ) );
55 };
56
57 // Total Earnings.
58 if ( $is_pro_reports ) {
59 $earnings = Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date );
60 $total_earnings = $earnings['total_earnings'] ?? 0;
61
62 if ( ! $is_all_time ) {
63 $previous_period_earnings = Analytics::get_earnings_by_user(
64 $user->ID,
65 '',
66 $previous_dates['previous_start_date'],
67 $previous_dates['previous_end_date']
68 )['total_earnings'] ?? 0;
69 }
70 } else {
71 $total_earnings = WithdrawModel::get_withdraw_summary( $user->ID )->total_income ?? 0;
72
73 if ( ! $is_all_time ) {
74 $previous_period_earnings = WithdrawModel::get_withdraw_summary(
75 $user->ID,
76 $date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )
77 )->total_income ?? 0;
78 }
79 }
80
81 // Total Courses.
82 $total_courses = CourseModel::get_course_count_by_date( $start_date, $end_date, $user->ID );
83 $previous_period_courses = ! $is_all_time
84 ? CourseModel::get_course_count_by_date( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID )
85 : 0;
86
87 // Total Students.
88 $total_students = tutor_utils()->get_total_students_by_instructor( $user->ID, $date_range( $start_date, $end_date ) );
89 $previous_period_students = ! $is_all_time
90 ? tutor_utils()->get_total_students_by_instructor( $user->ID, $date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] ) )
91 : 0;
92
93
94 // Total Ratings.
95 $total_ratings_where = ! $is_all_time ? $date_range( $start_date, $end_date ) : array();
96 $total_ratings = tutor_utils()->get_instructor_ratings( $user->ID, $total_ratings_where );
97 $previous_period_ratings = ! $is_all_time
98 ? tutor_utils()->get_instructor_ratings( $user->ID, $date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] ) )
99 : (object) array( 'rating_avg' => 0 );
100
101 /**
102 * -------------------------
103 * Hover (comparison) data
104 * Only for Pro Reports and “All Time” is not chosen.
105 * -------------------------
106 */
107 $total_earnings_state_card_details = array();
108 $total_courses_state_card_details = array();
109 $total_students_state_card_details = array();
110 $total_ratings_state_card_details = array();
111
112 if ( $tutor_pro_enabled && ! $is_all_time ) {
113 $total_earnings_state_card_details = $stat( $total_earnings, $previous_period_earnings, $previous_dates );
114 $total_courses_state_card_details = $stat( $total_courses, $previous_period_courses, $previous_dates );
115 $total_students_state_card_details = $stat( $total_students, $previous_period_students, $previous_dates );
116 $total_ratings_state_card_details = $stat( $total_ratings->rating_avg, $previous_period_ratings->rating_avg, $previous_dates );
117 }
118
119 /**
120 * -------------------------
121 * Stat cards
122 * -------------------------
123 */
124 $stat_cards = array(
125 array(
126 'variation' => 'brand',
127 'title' => esc_html__( 'Total Earnings', 'tutor' ),
128 'icon' => Icon::EARNING,
129 'value' => tutor_utils()->tutor_price( $total_earnings ?? 0 ),
130 'hover_content' => $total_earnings_state_card_details,
131 ),
132 array(
133 'variation' => 'exception1',
134 'title' => esc_html__( 'Total Courses', 'tutor' ),
135 'icon' => Icon::COURSES,
136 'value' => $total_courses,
137 'hover_content' => $total_courses_state_card_details,
138 ),
139 array(
140 'variation' => 'exception5',
141 'title' => esc_html__( 'Total Students', 'tutor' ),
142 'icon' => Icon::PASSED,
143 'value' => $total_students,
144 'hover_content' => $total_students_state_card_details,
145 ),
146 array(
147 'variation' => 'exception4',
148 'title' => esc_html__( 'Avg. Rating', 'tutor' ),
149 'icon' => Icon::STAR_LINE,
150 'value' => $total_ratings->rating_avg,
151 'hover_content' => $total_ratings_state_card_details,
152 ),
153 );
154
155
156 /**
157 * -------------------------
158 * Graph data (only for pro)
159 * -------------------------
160 */
161 if ( $is_pro_reports ) {
162 $enrollments = Analytics::get_total_students_by_user( $user->ID, '', $start_date, $end_date );
163
164 $overview_chart_data = array(
165 'earnings' => array( 0 ),
166 'enrolled' => array( 0 ),
167 'labels' => array( '' ),
168 'currency' => tutor_utils()->get_monetization_currency_config(),
169 'enrollment_date' => array( '' ),
170 'earning_date' => array( '' ),
171 );
172
173 foreach ( $earnings['earnings'] as $item ) {
174 $overview_chart_data['earnings'][] = (float) ( $item->total ?? 0 );
175 $overview_chart_data['labels'][] = $item->label_name ?? '';
176 $overview_chart_data['earning_date'][] = ! empty( $item->date_format )
177 ? wp_date( 'M d', strtotime( $item->date_format ) ) : '';
178 }
179
180 foreach ( $enrollments['enrollments'] as $item ) {
181 $overview_chart_data['enrolled'][] = (float) ( $item->total ?? 0 );
182 $overview_chart_data['enrollment_date'][] = ! empty( $item->date_format )
183 ? wp_date( 'M d', strtotime( $item->date_format ) ) : '';
184 }
185
186 $overview_chart_data['earnings'][] = 0;
187 $overview_chart_data['enrolled'][] = 0;
188 $overview_chart_data['labels'][] = '';
189 $overview_chart_data['earning_date'][] = '';
190 $overview_chart_data['enrollment_date'][] = '';
191 }
192
193 /**
194 * ---------------------------------------------
195 * Course Completion Distribution (For All Time)
196 * ---------------------------------------------
197 */
198
199 if ( $is_all_time ) {
200 $distribution = Instructor::get_course_completion_distribution_data_by_instructor( $instructor_course_ids );
201
202 $course_completion_data = array(
203 'enrolled' => array(
204 'label' => esc_html__( 'Enrolled', 'tutor' ),
205 'value' => $distribution['enrolled'],
206 ),
207 'completed' => array(
208 'label' => esc_html__( 'Completed', 'tutor' ),
209 'value' => $distribution['completed'],
210 ),
211 'in_progress' => array(
212 'label' => esc_html__( 'In Progress', 'tutor' ),
213 'value' => $distribution['inprogress'],
214 ),
215 'inactive' => array(
216 'label' => esc_html__( 'Inactive', 'tutor' ),
217 'value' => $distribution['inactive'],
218 ),
219 'cancelled' => array(
220 'label' => esc_html__( 'Cancelled', 'tutor' ),
221 'value' => $distribution['cancelled'],
222 ),
223 );
224 }
225
226 // Top Performing Courses.
227 $args = array(
228 'start_date' => $start_date,
229 'end_date' => $end_date,
230 'order_by' => Input::get( 'type', 'revenue' ),
231 );
232
233 $top_performing_courses = Instructor::format_instructor_top_performing_courses(
234 Instructor::get_top_performing_courses_by_instructor( $user->ID, $args )
235 );
236
237 // Upcoming Live Tasks (all-time + pro only).
238 if ( $is_all_time && $tutor_pro_enabled ) {
239 $upcoming_tasks = Instructor::format_instructor_upcoming_live_tasks(
240 Instructor::get_instructor_upcoming_live_tasks( $user->ID )
241 );
242 }
243
244 // Recent Reviews.
245 $review_args = array( 'comment_approved' => 'approved' );
246 if ( ! $is_all_time ) {
247 $review_args = $date_range( $start_date, $end_date );
248 }
249 $reviews = tutor_utils()->get_reviews_by_instructor( $user->ID, 0, 3, '', '', $review_args );
250 $recent_reviews = Instructor::format_instructor_recent_reviews( $reviews->results );
251
252
253 /**
254 * ------------------------------------
255 * Sortable sections data preparation
256 * ------------------------------------
257 */
258 $saved_order = get_user_meta( get_current_user_id(), '_tutor_instructor_home_sections_order', true );
259 $saved_visibility = get_user_meta( get_current_user_id(), '_tutor_instructor_home_sections_visibility', true );
260
261 $sortable_sections = array(
262 array(
263 'id' => 'current_stats',
264 'label' => esc_html__( 'Current Stats', 'tutor' ),
265 'is_active' => $saved_visibility['current_stats'] ?? true,
266 'order' => $saved_order['current_stats'] ?? 0,
267 'data' => true,
268 ),
269 array(
270 'id' => 'overview_chart',
271 'label' => esc_html__( 'Earnings Over Time', 'tutor' ),
272 'is_active' => $saved_visibility['overview_chart'] ?? true,
273 'order' => $saved_order['overview_chart'] ?? 1,
274 'data' => ! empty( $overview_chart_data ),
275 ),
276 array(
277 'id' => 'course_completion_and_leader',
278 'label' => esc_html__( 'Course Completion Rate', 'tutor' ),
279 'is_active' => $saved_visibility['course_completion_and_leader'] ?? true,
280 'order' => $saved_order['course_completion_and_leader'] ?? 2,
281 'data' => ! empty( $course_completion_data ),
282 ),
283 array(
284 'id' => 'top_performing_courses',
285 'label' => esc_html__( 'Top Performing Courses', 'tutor' ),
286 'is_active' => $saved_visibility['top_performing_courses'] ?? true,
287 'order' => $saved_order['top_performing_courses'] ?? 3,
288 'data' => ! empty( $top_performing_courses ),
289 ),
290 array(
291 'id' => 'upcoming_tasks_and_activity',
292 'label' => esc_html__( 'Upcoming Tasks', 'tutor' ),
293 'is_active' => $saved_visibility['upcoming_tasks_and_activity'] ?? true,
294 'order' => $saved_order['upcoming_tasks_and_activity'] ?? 4,
295 'data' => ! empty( $upcoming_tasks ),
296 ),
297 array(
298 'id' => 'recent_reviews',
299 'label' => esc_html__( 'Recent Student Reviews', 'tutor' ),
300 'is_active' => $saved_visibility['recent_reviews'] ?? true,
301 'order' => $saved_order['recent_reviews'] ?? 5,
302 'data' => ! empty( $recent_reviews ),
303 ),
304 );
305
306 // Remove sections which don't have data to show.
307 $sortable_sections = array_filter(
308 $sortable_sections,
309 fn( $section ) => $section['data']
310 );
311
312 usort(
313 $sortable_sections,
314 function ( $a, $b ) {
315 return $a['order'] <=> $b['order'];
316 }
317 );
318
319 $sortable_sections_defaults = array_reduce(
320 $sortable_sections,
321 function ( $carry, $section ) {
322 $carry[ $section['id'] ] = $section['is_active'] ?? false;
323 return $carry;
324 },
325 array()
326 );
327
328 $sortable_sections_ids = array_reduce(
329 $sortable_sections,
330 function ( $carry, $section ) {
331 $carry[ $section['order'] ] = $section['id'];
332 return $carry;
333 },
334 array()
335 );
336 ?>
337
338 <form x-data='tutorForm({
339 id: "sortable-sections",
340 mode: "onBlur",
341 defaultValues: <?php echo wp_json_encode( $sortable_sections_defaults ); ?>
342 })'
343 x-bind="getFormBindings()"
344 class="tutor-flex tutor-flex-column tutor-gap-6"
345 >
346 <!-- Filters -->
347 <div class="tutor-flex tutor-justify-between tutor-items-center">
348 <?php if ( $tutor_pro_enabled ) : ?>
349 <?php DateFilter::make()->type( DateFilter::TYPE_RANGE )->render(); ?>
350 <?php endif; ?>
351
352 <div class="tutor-dashboard-home-sort" x-data="tutorPopover({ placement: '<?php echo esc_js( $tutor_pro_enabled ? 'bottom-end' : 'bottom-start' ); ?>' })">
353 <button
354 x-ref="trigger"
355 @click="toggle()"
356 class="tutor-btn tutor-btn-outline tutor-btn-small tutor-btn-icon"
357 aria-label="<?php esc_attr_e( 'Filter dashboard sections', 'tutor' ); ?>"
358 >
359 <?php SvgIcon::make()->name( Icon::FILTER_2 )->render(); ?>
360 </button>
361
362 <div
363 x-ref="content"
364 x-show="open"
365 x-cloak
366 x-transition.origin.top.right
367 @click.outside="handleClickOutside()"
368 class="tutor-popover tutor-popover-bottom"
369 >
370 <div
371 class="tutor-popover-menu"
372 x-data='tutorSortableSections(
373 <?php echo wp_json_encode( $sortable_sections_ids ); ?>
374 )'
375 >
376 <?php foreach ( $sortable_sections as $section ) : ?>
377 <div
378 data-id="<?php echo esc_attr( $section['id'] ); ?>"
379 class="tutor-popover-menu-item"
380 >
381 <button data-grab>
382 <?php SvgIcon::make()->name( Icon::DRAG_VERTICAL )->size( 16 )->render(); ?>
383 </button>
384 <?php
385 InputField::make()
386 ->type( InputType::CHECKBOX )
387 ->name( "$section[id]" )
388 ->label( $section['label'] )
389 ->attr( 'x-bind', "\$el.closest('[data-dnd-placeholder]') ? {} : register('{$section['id']}')" )
390 ->attr( '@click.stop', 'handleCheckboxClick(event)' )
391 ->render();
392 ?>
393 </div>
394 <?php endforeach; ?>
395 </div>
396 </div>
397 </div>
398 </div>
399
400 <?php foreach ( $sortable_sections as $section ) : ?>
401 <?php if ( 'current_stats' === $section['id'] ) : ?>
402 <!-- Stat cards -->
403 <div
404 data-section-id="current_stats"
405 class="tutor-flex tutor-flex-wrap tutor-gap-5 tutor-z-positive"
406 x-show="watch('current_stats')"
407 x-cloak
408 >
409 <?php foreach ( $stat_cards as $card ) : ?>
410 <div class="tutor-flex-1">
411 <?php
412 tutor_load_template(
413 'dashboard.instructor.analytics.stat-card',
414 array(
415 'variation' => $card['variation'] ?? 'enrolled',
416 'card_title' => $card['title'] ?? '',
417 'icon' => $card['icon'] ?? '',
418 'icon_size' => $card['icon_size'] ?? 20,
419 'value' => $card['value'] ?? '',
420 'content' => $card['content'] ?? '',
421 'hover_content' => $card['hover_content'] ?? array(),
422 )
423 );
424 ?>
425 </div>
426 <?php endforeach; ?>
427 </div>
428 <?php endif; ?>
429
430 <!-- Overview Chart -->
431 <?php
432 if ( 'overview_chart' === $section['id'] && $overview_chart_data ) :
433 tutor_load_template(
434 'dashboard.instructor.home.overview-chart',
435 array(
436 'overview_chart_data' => $overview_chart_data,
437 )
438 );
439 endif;
440 ?>
441
442 <?php if ( 'course_completion_and_leader' === $section['id'] && ! empty( $course_completion_data ) ) : ?>
443 <div
444 data-section-id="course_completion_and_leader"
445 class="tutor-flex tutor-gap-6"
446 x-show="watch('course_completion_and_leader')"
447 x-cloak
448 >
449 <!-- Course Completion Chart -->
450 <?php
451 tutor_load_template(
452 'dashboard.instructor.home.course-completion-chart',
453 array(
454 'course_completion_data' => $course_completion_data,
455 )
456 );
457 ?>
458 </div>
459 <?php endif; ?>
460
461 <!-- Top Performing Courses -->
462 <?php if ( 'top_performing_courses' === $section['id'] && ! empty( $top_performing_courses ) ) : ?>
463 <div
464 data-section-id="top_performing_courses"
465 class="tutor-dashboard-home-card"
466 x-show="watch('top_performing_courses')"
467 x-cloak
468 >
469 <div class="tutor-flex tutor-row tutor-justify-between tutor-items-center tutor-gap-9">
470 <div class="tutor-small">
471 <?php esc_html_e( 'Top Performing Courses', 'tutor' ); ?>
472 </div>
473
474 <!-- Sorting -->
475 <?php
476 $data = array(
477 'options' => array(
478 'revenue' => __( 'Revenue', 'tutor' ),
479 'student' => __( 'Student', 'tutor' ),
480 ),
481 'selected' => Input::get( 'type', 'revenue' ),
482 );
483 tutor_load_template(
484 'dashboard.instructor.home.top-performing-course-filter',
485 $data,
486 );
487 ?>
488 </div>
489
490 <div class="tutor-dashboard-home-card-body tutor-gap-4">
491 <?php foreach ( $top_performing_courses as $item_key => $item ) : ?>
492 <?php
493 tutor_load_template(
494 'dashboard.instructor.home.top-performing-course-item',
495 array(
496 'item_key' => $item_key,
497 'item' => $item,
498 ),
499 )
500 ?>
501 <?php endforeach; ?>
502 </div>
503 </div>
504 <?php endif; ?>
505
506 <!-- Upcoming Task And Activity -->
507 <?php if ( 'upcoming_tasks_and_activity' === $section['id'] && ! empty( $upcoming_tasks ) ) : ?>
508 <div
509 data-section-id="upcoming_tasks_and_activity"
510 class="tutor-flex tutor-gap-6"
511 x-show="watch('upcoming_tasks_and_activity')"
512 x-cloak
513 >
514 <!-- Upcoming Tasks -->
515 <div class="tutor-dashboard-home-card tutor-flex-1">
516 <div class="tutor-small">
517 <?php esc_html_e( 'Upcoming Tasks', 'tutor' ); ?>
518 </div>
519
520 <div class="tutor-dashboard-home-card-body tutor-gap-4">
521 <?php foreach ( $upcoming_tasks as $item ) : ?>
522 <?php
523 tutor_load_template(
524 'dashboard.instructor.home.upcoming-task-item',
525 array( 'item' => $item )
526 );
527 ?>
528 <?php endforeach; ?>
529 </div>
530 </div>
531 </div>
532 <?php endif; ?>
533
534 <!-- Recent Student Reviews -->
535 <?php if ( 'recent_reviews' === $section['id'] && ! empty( $recent_reviews ) ) : ?>
536 <div
537 data-section-id="recent_reviews"
538 class="tutor-dashboard-home-card"
539 x-show="watch('recent_reviews')"
540 x-cloak
541 >
542 <div class="tutor-small">
543 <?php esc_html_e( 'Recent Student Reviews', 'tutor' ); ?>
544 </div>
545
546 <div class="tutor-dashboard-home-card-body tutor-gap-6">
547 <?php foreach ( $recent_reviews as $review ) : ?>
548 <?php
549 tutor_load_template(
550 'dashboard.instructor.home.recent-student-review-item',
551 array( 'review' => $review ),
552 );
553 ?>
554 <?php endforeach; ?>
555 </div>
556 </div>
557 <?php endif; ?>
558 <?php endforeach; ?>
559 </form>
560