From 74f350546175882da93b4d30e7346cb1fe6f9c12 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 4 Dec 2025 18:05:26 +0600 Subject: [PATCH 001/109] tab --- components/Tabs.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/Tabs.php b/components/Tabs.php index 830243fa63..fd93312bf6 100644 --- a/components/Tabs.php +++ b/components/Tabs.php @@ -194,7 +194,9 @@ public function render(): string { :disabled="tab.disabled ? true : false" @click="selectTab(tab.id)" > - + From 8ced7af9ed98739c1233ce55b0c30693eca5a634 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 12 Dec 2025 18:02:47 +0600 Subject: [PATCH 002/109] overview earning graph --- .../scss/frontend/dashboard/_stat-card.scss | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/assets/scss/frontend/dashboard/_stat-card.scss b/assets/scss/frontend/dashboard/_stat-card.scss index d0f3da3649..55f2aa5a6d 100644 --- a/assets/scss/frontend/dashboard/_stat-card.scss +++ b/assets/scss/frontend/dashboard/_stat-card.scss @@ -76,5 +76,36 @@ color: $tutor-text-exception4; } } + + &-courses { + .tutor-stat-card-icon { + color: $tutor-text-success; + } + + .tutor-stat-card-value { + color: $tutor-text-success; + } + } + + + &-students { + .tutor-stat-card-icon { + color: $tutor-text-brand; + } + + .tutor-stat-card-value { + color: $tutor-text-brand; + } + } + + &-reviews { + .tutor-stat-card-icon { + color: $tutor-warning-400; + } + + .tutor-stat-card-value { + color: $tutor-warning-400; + } + } } From 2c1319acfcc960092c137bef5c4c7d5f98b7a614 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 15 Dec 2025 18:02:57 +0600 Subject: [PATCH 003/109] graph tab --- templates/demo-components/dynamic-components.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/demo-components/dynamic-components.php b/templates/demo-components/dynamic-components.php index 0c7ae5463e..2f68261690 100644 --- a/templates/demo-components/dynamic-components.php +++ b/templates/demo-components/dynamic-components.php @@ -13,8 +13,8 @@ use Tutor\Components\Avatar; use Tutor\Components\Badge; use Tutor\Components\Button; -use Tutor\Components\Constants\Positions; use Tutor\Components\Constants\Size; +use Tutor\Components\Constants\Positions; use Tutor\Components\Constants\Variant; use Tutor\Components\InputField; use Tutor\Components\Modal; From 533eb5315cb038e401092f7c4230bd0009311f4a Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Wed, 17 Dec 2025 05:54:58 +0600 Subject: [PATCH 004/109] popular courses --- components/Table.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/Table.php b/components/Table.php index 5c26847715..d7c79d6191 100644 --- a/components/Table.php +++ b/components/Table.php @@ -177,8 +177,9 @@ protected function render_table_headings(): string { foreach ( $this->cell_headers as $heading ) { $headings .= sprintf( - '%1$s', - apply_filters( 'tutor_table_heading', $heading['content'] ) + '%s', + $heading['class'], + apply_filters( 'tutor_table_heading',$heading['content'] ) ); } From a528f5821901444f822e4ab737d8d45d5fcf05aa Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 17 Dec 2025 15:43:41 +0600 Subject: [PATCH 005/109] Popular courses rating --- assets/icons/star-line.svg | 3 +++ assets/icons/star.svg | 4 ++- assets/react/v3/shared/icons/types.ts | 1 + classes/Icon.php | 1 + classes/Utils.php | 19 +++++++++----- .../dashboard/components/star-rating.php | 25 +++++++++++-------- 6 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 assets/icons/star-line.svg diff --git a/assets/icons/star-line.svg b/assets/icons/star-line.svg new file mode 100644 index 0000000000..2ff5fbdc10 --- /dev/null +++ b/assets/icons/star-line.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/star.svg b/assets/icons/star.svg index 9ba25c3572..7d82a100a8 100644 --- a/assets/icons/star.svg +++ b/assets/icons/star.svg @@ -1 +1,3 @@ - \ No newline at end of file + + + diff --git a/assets/react/v3/shared/icons/types.ts b/assets/react/v3/shared/icons/types.ts index a197c0c755..6a0106dcd9 100644 --- a/assets/react/v3/shared/icons/types.ts +++ b/assets/react/v3/shared/icons/types.ts @@ -308,6 +308,7 @@ export const icons = [ 'spinner', 'spreadsheet', 'star', + 'starLine', 'stepper', 'stopwatch', 'storeEye', diff --git a/classes/Icon.php b/classes/Icon.php index 72a50b39a5..9fdf057fe2 100644 --- a/classes/Icon.php +++ b/classes/Icon.php @@ -324,6 +324,7 @@ final class Icon { const SPINNER = 'spinner'; const SPREADSHEET = 'spreadsheet'; const STAR = 'star'; + const STAR_LINE = 'star-line'; const STEPPER = 'stepper'; const STOPWATCH = 'stopwatch'; const STORE_EYE = 'store-eye'; diff --git a/classes/Utils.php b/classes/Utils.php index 36cf936967..6b9d405189 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10662,28 +10662,31 @@ public function build_term_tree( $terms, $parent = 0 ) { * Render SVG icon * * @since 3.7.0 + * @since 4.0.0 added $return parameter. * * @param string $name Icon name. * @param integer $width Icon width. * @param integer $height Icon height. * @param array $attributes Custom attributes. + * @param bool $return Whether to return the SVG markup instead of echoing it. + * Default false (echo). * - * @return void + * @return string|null Returns the SVG markup when $return is true, otherwise null. */ - public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array() ) { + public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array(), $return = false ) :?string { $icon_path = tutor()->path . 'assets/icons/' . $name . '.svg'; if ( ! file_exists( $icon_path ) ) { - return; + return null; } $svg = file_get_contents( $icon_path ); if ( ! $svg ) { - return; + return null; } preg_match( '/]*viewBox="([^"]+)"[^>]*>(.*?)<\/svg>/is', $svg, $matches ); if ( ! $matches ) { - return; + return null; } list( $svg_tag, $view_box, $inner_svg ) = $matches; @@ -10699,7 +10702,11 @@ public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = $attr_string .= ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; } - printf( '%s', $attr_string, $inner_svg ); + if ( ! $return ) { + printf( '%s', $attr_string, $inner_svg ); + } + + return ''. $inner_svg .''; } /** diff --git a/templates/demo-components/dashboard/components/star-rating.php b/templates/demo-components/dashboard/components/star-rating.php index fa34715650..6b7d990048 100644 --- a/templates/demo-components/dashboard/components/star-rating.php +++ b/templates/demo-components/dashboard/components/star-rating.php @@ -1,4 +1,6 @@
= $i ) { - $icon_class_name = 'tutor-icon-star-bold tutor-icon-exception4'; - } elseif ( ( $rating - $i ) >= -0.5 ) { - $icon_class_name = 'tutor-icon-star-half-bold tutor-icon-exception4'; - } else { - $icon_class_name = 'tutor-icon-star-line tutor-icon-exception4'; - } - $final_class = ! empty( $icon_class ) ? $icon_class_name . ' ' . $icon_class : $icon_class_name; + $is_full = (int) $rating >= $i; + $is_half = ! $is_full && ( $rating >= ( $i - 0.5 ) ); + + $icon_name = $is_full + ? Icon::STAR + : ( $is_half ? Icon::STAR_LINE : Icon::STAR_LINE ); // Todo: Half star icon. + + $icon_html = tutor_utils()->render_svg_icon( $icon_name, 16, 16, array(), true ); // phpcs:ignore ?> - +
+ +
From 80a82a592f15037ea9a71b91e7349ba60608b91c Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 17 Dec 2025 16:13:41 +0600 Subject: [PATCH 006/109] =?UTF-8?q?Fix(=F0=9F=9B=A0=EF=B8=8F):=20SVG=20ico?= =?UTF-8?q?n=20return=20type=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- classes/Utils.php | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 6b9d405189..e941e7f91f 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -2916,7 +2916,7 @@ public function tutor_dashboard_pages() { // @TODO need to make it dynamic with account view as mode. // $view_mode = 'student'; // if ( User::is_admin() || User::is_instructor() ) { - // $view_mode = 'instructor'; + // $view_mode = 'instructor'; // } $student_nav_items = apply_filters( 'tutor_dashboard/nav_items', $this->default_menus() ); @@ -9391,7 +9391,7 @@ public function not_found_text(): string { */ public function instructor_menus(): array { $menus = array( - 'index' => array( + 'index' => array( 'title' => __( 'Home', 'tutor' ), 'auth_cap' => tutor()->instructor_role, 'icon' => Icon::HOME_FILL, @@ -9428,7 +9428,7 @@ public function instructor_menus(): array { 'icon' => Icon::NOTIFICATION, ), ); - + if ( $this->should_show_dicussion_menu() ) { $other_menus['discussions'] = array( 'title' => __( 'Discussions', 'tutor' ), @@ -9443,7 +9443,7 @@ public function instructor_menus(): array { /** * Should show the disscussion menu on the student * and instructor dashboard menu - * + * * @since 4.0.0 * * @return boolean @@ -9744,7 +9744,7 @@ public function clean_html_content( $content = '', $allowed = array() ) { * * @return string */ - public function get_svg_icon( $name = '', $width = 16, $height = 16, $attributes = array() ) { + public function get_svg_icon( $name = '', $width = 16, $height = 16, $attributes = array() ) { $icon_path = tutor()->path . 'assets/icons/' . $name . '.svg'; if ( ! file_exists( $icon_path ) ) { @@ -10668,12 +10668,12 @@ public function build_term_tree( $terms, $parent = 0 ) { * @param integer $width Icon width. * @param integer $height Icon height. * @param array $attributes Custom attributes. - * @param bool $return Whether to return the SVG markup instead of echoing it. - * Default false (echo). - * - * @return string|null Returns the SVG markup when $return is true, otherwise null. + * @param bool $return Whether to return the SVG markup instead of echoing it. + * Default false (echo). + * + * @return string|null Returns the SVG markup when `$return` is true, otherwise null. */ - public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array(), $return = false ) :?string { + public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array(), $return = false ):? string { $icon_path = tutor()->path . 'assets/icons/' . $name . '.svg'; if ( ! file_exists( $icon_path ) ) { return null; @@ -10704,6 +10704,7 @@ public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = if ( ! $return ) { printf( '%s', $attr_string, $inner_svg ); + return null; } return ''. $inner_svg .''; From f7ea00ff5133b5be293a8b813bec1bc114f2959b Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 17 Dec 2025 18:08:34 +0600 Subject: [PATCH 007/109] Course tab --- components/Constants/InputType.php | 3 +-- components/InputField.php | 6 ++++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/Constants/InputType.php b/components/Constants/InputType.php index e56b52ed87..9ebf471f77 100644 --- a/components/Constants/InputType.php +++ b/components/Constants/InputType.php @@ -36,6 +36,5 @@ abstract class InputType { public const PASSWORD = 'password'; public const NUMBER = 'number'; public const COLOR = 'color'; - + public const SEARCH = 'search'; } - diff --git a/components/InputField.php b/components/InputField.php index a50f66961f..dd85f2d9f8 100644 --- a/components/InputField.php +++ b/components/InputField.php @@ -859,6 +859,9 @@ public function get(): string { case InputType::SWITCH: $input_html = $this->render_switch(); break; + case InputType::SEARCH: + $input_html = $this->render_search(); + break; default: $input_html = $this->render_text_input(); break; @@ -889,4 +892,7 @@ public function get(): string { return $this->component_string; } + protected function render_search(){ + + } } From e7580ad9405073b82e2bc3fa654a1cfbb12f0496 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Thu, 18 Dec 2025 08:19:21 +0600 Subject: [PATCH 008/109] search field --- components/InputField.php | 84 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/components/InputField.php b/components/InputField.php index dd85f2d9f8..51342a0e86 100644 --- a/components/InputField.php +++ b/components/InputField.php @@ -893,6 +893,88 @@ public function get(): string { } protected function render_search(){ - + $input_id = ! empty( $this->id ) ? $this->id : $this->name; + + $input_classes = 'tutor-input'; + if ( ! empty( $this->left_icon ) ) { + $input_classes .= ' tutor-input-content-left'; + } + if ( ! empty( $this->right_icon ) ) { + $input_classes .= ' tutor-input-content-right'; + } + if ( $this->clearable ) { + $input_classes .= ' tutor-input-content-clear'; + } + + $input_attrs = sprintf( + 'type="%s" id="%s" name="%s" class="%s" %s', + esc_attr( $this->type ), + esc_attr( $input_id ), + esc_attr( $this->name ), + esc_attr( $input_classes ), + $this->render_attributes() + ); + + if ( ! empty( $this->placeholder ) ) { + $input_attrs .= sprintf( ' placeholder="%s"', esc_attr( $this->placeholder ) ); + } + + if ( ! empty( $this->value ) ) { + $input_attrs .= sprintf( ' value="%s"', esc_attr( $this->value ) ); + } + + $left_icon_html = ''; + if ( ! empty( $this->left_icon ) ) { + $left_icon_html = sprintf( + '
%s
', + $this->left_icon + ); + } + + $right_icon_html = ''; + if ( ! empty( $this->right_icon ) ) { + $right_icon_html = sprintf( + '
%s
', + $this->right_icon + ); + } + + $clear_button_html = ''; + if ( $this->clearable ) { + $clear_icon = ''; + if ( function_exists( 'tutor_utils' ) ) { + ob_start(); + tutor_utils()->render_svg_icon( 'cross', 16, 16 ); + $clear_icon = ob_get_clean(); + } + + $clear_button_html = sprintf( + '', + esc_attr( $this->name ), + $clear_icon + ); + + } + + return sprintf( + '
+ + %s + %s + %s +
+ ', + $input_attrs, + $left_icon_html, + $right_icon_html, + $clear_button_html, + ); } } From 9ce0612b6fe30c5389e4f567c7f285961c52164b Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 18 Dec 2025 18:03:26 +0600 Subject: [PATCH 009/109] course list --- .../frontend/dashboard/layout/_header.scss | 2 +- .../frontend/dashboard/layout/_layout.scss | 2 +- classes/Utils.php | 30 +++++++++++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/assets/scss/frontend/dashboard/layout/_header.scss b/assets/scss/frontend/dashboard/layout/_header.scss index 298fb3a006..88700cde85 100644 --- a/assets/scss/frontend/dashboard/layout/_header.scss +++ b/assets/scss/frontend/dashboard/layout/_header.scss @@ -13,7 +13,7 @@ &-inner { @include tutor-flex(row, center, space-between); height: 100%; - max-width: 726px; + max-width: 950px; margin: 0 auto; } diff --git a/assets/scss/frontend/dashboard/layout/_layout.scss b/assets/scss/frontend/dashboard/layout/_layout.scss index 3c3a510197..87a2107448 100644 --- a/assets/scss/frontend/dashboard/layout/_layout.scss +++ b/assets/scss/frontend/dashboard/layout/_layout.scss @@ -21,7 +21,7 @@ } .tutor-dashboard-page { - max-width: 758px; + max-width: 1000px; margin: 0 auto; padding-inline: $tutor-spacing-6; diff --git a/classes/Utils.php b/classes/Utils.php index e941e7f91f..55f443531c 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10670,10 +10670,10 @@ public function build_term_tree( $terms, $parent = 0 ) { * @param array $attributes Custom attributes. * @param bool $return Whether to return the SVG markup instead of echoing it. * Default false (echo). - * + * * @return string|null Returns the SVG markup when `$return` is true, otherwise null. */ - public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array(), $return = false ):? string { + public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array(), $return = false ): ?string { $icon_path = tutor()->path . 'assets/icons/' . $name . '.svg'; if ( ! file_exists( $icon_path ) ) { return null; @@ -10707,7 +10707,7 @@ public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = return null; } - return ''. $inner_svg .''; + return '' . $inner_svg . ''; } /** @@ -10745,4 +10745,28 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U return null; } + + public function render_template( $template, $data, $type, $tutor_pro = false ) { + + ob_start(); + + switch ( $type ) { + case 'include': + include $template; + break; + + case 'custom_template': + tutor_load_template_from_custom_path( $template, $data ); + break; + + case 'template': + tutor_load_template( $template, $data, $tutor_pro ); + break; + + default: + break; + } + + return (string) ob_get_clean(); + } } From 7bda0c6a41a97eb5f547561d5a46274919f24fe4 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 19 Dec 2025 17:08:10 +0600 Subject: [PATCH 010/109] Pagination --- templates/dashboard/elements/pagination.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dashboard/elements/pagination.php b/templates/dashboard/elements/pagination.php index 5dc27bf97d..bfc1270ec7 100644 --- a/templates/dashboard/elements/pagination.php +++ b/templates/dashboard/elements/pagination.php @@ -42,7 +42,7 @@ } if ( ( isset( $data['total_page'] ) && $data['total_page'] ) || ( isset( $data['total_items'] ) && $data['total_items'] ) ) : ?> -
diff --git a/templates/dashboard.php b/templates/dashboard.php index 66a203bc0c..b51380f90f 100644 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -10,6 +10,9 @@ $is_by_short_code = isset( $is_shortcode ) && true === $is_shortcode; if ( ! $is_by_short_code && ! defined( 'OTLMS_VERSION' ) ) { + ?> + + course_post_type ) ); +$order = Input::get( 'order', 'DESC' ); +$search = Input::get( 'search', '' ); + // Get counts for course tabs. $count_map = array( - 'publish' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_PUBLISH, 0, 0, true, $post_type ), - 'pending' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_PENDING, 0, 0, true, $post_type ), - 'draft' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_DRAFT, 0, 0, true, $post_type ), - 'future' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_FUTURE, 0, 0, true, $post_type ), + 'publish' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_PUBLISH, 0, 0, true, $post_type, $search ), + 'pending' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_PENDING, 0, 0, true, $post_type, $search ), + 'draft' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_DRAFT, 0, 0, true, $post_type, $search ), + 'future' => CourseModel::get_courses_by_instructor( $current_user_id, CourseModel::STATUS_FUTURE, 0, 0, true, $post_type, $search ), ); -$course_archive_arg = isset( $GLOBALS['tutor_course_archive_arg'] ) ? $GLOBALS['tutor_course_archive_arg']['column_per_row'] : null; -$courseCols = null === $course_archive_arg ? tutor_utils()->get_option( 'courses_col_per_row', 4 ) : $course_archive_arg; $per_page = tutor_utils()->get_option( 'courses_per_page', 10 ); -$paged = Input::get( 'current_page', 1, Input::TYPE_INT ); -$offset = $per_page * ( $paged - 1 ); -$results = CourseModel::get_courses_by_instructor( $current_user_id, $status, $offset, $per_page, false, $post_type ); +$current_page = Input::get( 'current_page', 1, Input::TYPE_INT ); +$offset = $per_page * ( $current_page - 1 ); +$results = CourseModel::get_courses_by_instructor( $current_user_id, $status, $offset, $per_page, false, $post_type, $search, $order ); $show_course_delete = true; $post_type_query = Input::get( 'type', '' ); $post_type_args = $post_type_query ? array( 'type' => $post_type_query ) : array(); -$tabs = array( - 'publish' => array( - 'title' => __( 'Publish', 'tutor' ), - 'link' => 'my-courses', - ), - 'pending' => array( - 'title' => __( 'Pending', 'tutor' ), - 'link' => 'my-courses/pending-courses', - ), - 'draft' => array( - 'title' => __( 'Draft', 'tutor' ), - 'link' => 'my-courses/draft-courses', - ), - 'future' => array( - 'title' => __( 'Schedule', 'tutor' ), - 'link' => 'my-courses/schedule-courses', +$current_url = add_query_arg( $post_type_args, tutor_utils()->get_tutor_dashboard_page_permalink( $active_tab ) ); + +$nav_items = array( + array( + 'type' => 'dropdown', + 'active' => true, + 'options' => array( + array( + 'label' => __( 'Published', 'tutor' ), + 'count' => $count_map['publish'] ?? 0, + 'url' => add_query_arg( $post_type_args, tutor_utils()->get_tutor_dashboard_page_permalink( 'my-courses' ) ), + 'active' => 'my-courses' === $active_tab, + ), + array( + 'label' => __( 'Pending', 'tutor' ), + 'count' => $count_map['pending'] ?? 0, + 'url' => add_query_arg( $post_type_args, tutor_utils()->get_tutor_dashboard_page_permalink( 'my-courses/pending-courses' ) ), + 'active' => 'my-courses/pending-courses' === $active_tab, + ), + array( + 'label' => __( 'Draft', 'tutor' ), + 'count' => $count_map['draft'] ?? 0, + 'url' => add_query_arg( $post_type_args, tutor_utils()->get_tutor_dashboard_page_permalink( 'my-courses/draft-courses' ) ), + 'active' => 'my-courses/draft-courses' === $active_tab, + ), + array( + 'label' => __( 'Schedule', 'tutor' ), + 'count' => $count_map['future'] ?? 0, + 'url' => add_query_arg( $post_type_args, tutor_utils()->get_tutor_dashboard_page_permalink( 'my-courses/schedule-courses' ) ), + 'active' => 'my-courses/schedule-courses' === $active_tab, + ), + ), ), ); @@ -70,294 +95,307 @@ } ?> -
-
- -
- -
-
-
    - $tab ) : ?> -
  • - - - -
  • - - -
- +
+
+
+ variant( Variant::PRIMARY )->size( Size::SMALL )->items( $nav_items )->render(); ?> +
+ + +
- - - url . 'assets/images/placeholder.svg'; +
+ tutor_empty_state( tutor_utils()->not_found_text() ); - } else { + SearchFilter::make() + ->form_id( 'tutor-my-courses-search-form' ) + ->placeholder( __( 'Search courses...', 'tutor' ) ) + ->size( Size::SMALL ) + ->action( $current_url ) + ->hidden_inputs( $hidden_inputs ) + ->render(); ?> -
- nonce_action ); - foreach ( $results as $post ) : - setup_postdata( $post ); - - $avg_rating = tutor_utils()->get_course_rating()->rating_avg; - $tutor_course_img = get_tutor_course_thumbnail_src(); - $id_string_delete = 'tutor_my_courses_delete_' . $post->ID; - $row_id = 'tutor-dashboard-my-course-' . $post->ID; - $course_duration = get_tutor_course_duration_context( $post->ID, true ); - $course_students = tutor_utils()->count_enrolled_users_by_course(); - $is_main_instructor = CourseModel::is_main_instructor( $post->ID ); - $course_edit_link = apply_filters( 'tutor_dashboard_course_list_edit_link', tutor_utils()->course_edit_link( $post->ID, tutor()->has_pro ? 'frontend' : 'backend' ), $post ); - ?> +
+ + order( $order )->render(); ?> +
+
-
- -
- <?php the_title(); ?> + + title( 'No Courses Found' )->render(); ?> + +
+ nonce_action ); + foreach ( $results as $post ) : + setup_postdata( $post ); + $tutor_course_img = get_tutor_course_thumbnail_src(); + $course_duration = tutor_utils()->get_course_duration( $post->ID, false ); + $course_students = tutor_utils()->count_enrolled_users_by_course(); + $is_main_instructor = CourseModel::is_main_instructor( $post->ID ); + $course_edit_link = apply_filters( 'tutor_dashboard_course_list_edit_link', tutor_utils()->course_edit_link( $post->ID, tutor()->has_pro ? 'frontend' : 'backend' ), $post ); + ?> +
+
+
+ ID ); ?> + <?php the_title(); ?> + - - - -
- -
+
+
-
- - - -
- -
- + +
+ render_svg_icon( Icon::RELOAD_2, 14, 14, array( 'class' => 'tutor-icon-brand' ) ); ?> + -
- + + + + + -
+
+ +
+ render_svg_icon( Icon::PASSED, 14, 14 ); ?> + +
+ + -
- - +
+ render_svg_icon( Icon::TIME, 14, 14 ); ?> + +
+ +
+ +
+
+ +
+ +
+ - -
-
-
-
- - -
-
- -
+ $per_page ) : ?> +
+ current( $current_page ) + ->total( $count_map[ $status ] ) + ->limit( $per_page ) + ->render(); + ?> +
+ +
-
-
+ +
+
+
diff --git a/templates/demo-components/components/toast.php b/templates/demo-components/components/toast.php new file mode 100644 index 0000000000..03fe32fae7 --- /dev/null +++ b/templates/demo-components/components/toast.php @@ -0,0 +1,189 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +?> +
+

Toast Notifications

+ + +
+

+ Toast notifications provide non-intrusive feedback to users. The toast container is automatically injected into the DOM when you trigger your first toast - no manual setup required! +

+
+
+ + + All toasts include a default title based on their type (Success, Error, Warning, Info). You can customize this title if needed. + +
+
+
+ + +
+

Toast Variants

+

+ Four toast variants for different notification types: Default (Info), Success, Warning, and Error. +

+
+
+ + + + +
+
+
+ + +
+

Toast with Custom Title

+

+ Toast notifications automatically show a title based on the variant (Success, Error, Warning, Info). You can override this with a custom title. +

+
+
+ + +
+
+
+ + +
+

Custom Duration

+

+ Control how long toasts stay visible. Default is 5 seconds. +

+
+
+ + + + +
+
+
+ + +
+

Stacking Toasts

+

+ Multiple toasts stack vertically and can be shown simultaneously. +

+
+
+ + +
+
+
+ + +
+

Usage

+

+ How to use toast notifications in your code. +

+
+
// Show a success toast (default title: "Success")
+TutorCore.toast.success('Operation completed!');
+
+// Show an error toast (default title: "Error")
+TutorCore.toast.error('Something went wrong');
+
+// Show a warning toast (default title: "Warning")
+TutorCore.toast.warning('Please be careful');
+
+// Show an info toast (default title: "Info")
+TutorCore.toast.info('Here is some information');
+
+// Custom title
+TutorCore.toast.show('Your profile has been updated', { 
+  type: 'success', 
+  title: 'Profile Updated' 
+});
+
+// Custom duration (in milliseconds)
+TutorCore.toast.success('Quick message', 2000);
+
+// Persistent toast (doesn't auto-dismiss)
+TutorCore.toast.show('Important message', { type: 'warning', duration: 0 });
+
+// Clear all toasts
+TutorCore.toast.clear();
+
+
+
diff --git a/templates/demo-components/dashboard/components/nav-mobile.php b/templates/demo-components/dashboard/components/nav-mobile.php index e1d5b15fa8..1c36b43b59 100644 --- a/templates/demo-components/dashboard/components/nav-mobile.php +++ b/templates/demo-components/dashboard/components/nav-mobile.php @@ -71,7 +71,7 @@ alt="" width="20" height="20" - class="tutor-rounded-full " + class="tutor-avatar tutor-avatar-20 " > render_svg_icon( ( $key === $active_nav ) ? $item['active_icon'] : $item['icon'], 20, 20 ); ?> diff --git a/templates/demo-components/dynamic-components.php b/templates/demo-components/dynamic-components.php index f42b1f6d36..115bfec31d 100644 --- a/templates/demo-components/dynamic-components.php +++ b/templates/demo-components/dynamic-components.php @@ -40,8 +40,7 @@ Button::make()->label( 'I am a button' )->variant(Variant::DESTRUCTIVE_SOFT )->attr( 'class', 'tutor-btn-loading' )->render(); // phpcs:ignore Button::make()->label( 'I am a button' )->variant( Variant::OUTLINE )->render(); // phpcs:ignore Button::make()->label( 'I am a button' )->variant( Variant::PENDING )->render(); // phpcs:ignore - Button::make()->size( Size::LARGE )->icon( - 'SVG' )->variant( Variant::COMPLETED )->render(); // phpcs:ignore + Button::make()->size( Size::LARGE )->icon( Icon::CHECK )->variant( Variant::COMPLETED )->render(); // phpcs:ignore Button::make()->attr( 'class', 'tutor-btn-block' )->label( 'I am a block button' )->variant( Variant::PRIMARY_SOFT )->render(); ?> @@ -57,10 +56,7 @@ Button::make()->label( 'I am a button' )->variant(Variant::DESTRUCTIVE_SOFT )->attr( 'class', 'tutor-btn-loading' )->render(); // phpcs:ignore Button::make()->label( 'I am a button' )->variant( Variant::OUTLINE )->render(); // phpcs:ignore Button::make()->label( 'I am a button' )->variant( Variant::PENDING )->render(); // phpcs:ignore - Button::make()->size( Size::LARGE )->icon( - ' - - ' )->variant( Variant::COMPLETED )->render(); // phpcs:ignore + Button::make()->size( Size::LARGE )->icon( Icon::CHECK )->variant( Variant::COMPLETED )->render(); // phpcs:ignore Button::make()->attr( 'class', 'tutor-btn-block' )->label( 'I am a block button' )->variant( Variant::PRIMARY_SOFT )->render(); ?> @@ -99,7 +95,7 @@

Badge


 		<?php
-			Badge::make()->label( 'Primary' )->variant( Variant::PRIMARY )->icon( 'svg' )->render();
+			Badge::make()->label( 'Primary' )->variant( Variant::PRIMARY )->icon( Icon::CHECK )->render();
 			Badge::make()->label( 'Points: 20' )->variant( Variant::SECONDARY )->render();
 			Badge::make()->label( 'Completed' )->variant( Variant::COMPLETED )->circle()->render();
 			Badge::make()->label( 'Cancelled' )->variant( Variant::CANCELLED )->circle()->render();
@@ -107,7 +103,7 @@
 		
label( 'Primary' )->variant( Variant::PRIMARY )->icon( '' )->render(); + Badge::make()->label( 'Primary' )->variant( Variant::PRIMARY )->icon( Icon::CHECK )->render(); Badge::make()->label( 'Points: 20' )->variant( Variant::SECONDARY )->render(); Badge::make()->label( 'Completed' )->variant( Variant::COMPLETED )->circle()->render(); Badge::make()->label( 'Cancelled' )->variant( Variant::CANCELLED )->circle()->render(); diff --git a/templates/demo-components/playground.php b/templates/demo-components/playground.php index a3996f4d5a..444b9d2d91 100644 --- a/templates/demo-components/playground.php +++ b/templates/demo-components/playground.php @@ -93,6 +93,7 @@ + From 5319e83de5beadab5f5d440edc80cd0b10495455 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Fri, 26 Dec 2025 08:34:08 +0600 Subject: [PATCH 018/109] student details --- assets/src/scss/frontend/dashboard/_profile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/src/scss/frontend/dashboard/_profile.scss b/assets/src/scss/frontend/dashboard/_profile.scss index 0d49ff00ab..eb62fbf329 100644 --- a/assets/src/scss/frontend/dashboard/_profile.scss +++ b/assets/src/scss/frontend/dashboard/_profile.scss @@ -21,7 +21,7 @@ border: 1px solid $tutor-border-idle; border-radius: $tutor-radius-2xl; padding: $tutor-spacing-6; - margin-top: $tutor-spacing-9; + margin-top: $tutor-spacing-6; @include tutor-breakpoint-down(sm) { margin-top: $tutor-spacing-5; From ab337598ea1c9a3c847a2cf284dd52ca43ff80b2 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 26 Dec 2025 13:24:03 +0600 Subject: [PATCH 019/109] analytics component added --- .../instructor/analytics/star-rating.php | 45 +++++++++++++++ .../instructor/analytics/stat-card.php | 55 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 templates/dashboard/instructor/analytics/star-rating.php create mode 100644 templates/dashboard/instructor/analytics/stat-card.php diff --git a/templates/dashboard/instructor/analytics/star-rating.php b/templates/dashboard/instructor/analytics/star-rating.php new file mode 100644 index 0000000000..92eb9b04d6 --- /dev/null +++ b/templates/dashboard/instructor/analytics/star-rating.php @@ -0,0 +1,45 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +use TUTOR\Icon; + +if ( ! defined( 'ABSPATH' ) ) { + exit; +} + +// Default values - all data must be passed from parent. +$rating = isset( $rating ) ? floatval( $rating ) : 0.00; +$wrapper_class = isset( $wrapper_class ) ? $wrapper_class : 'tutor-ratings-stars tutor-flex tutor-items-center tutor-gap-2'; +$icon_class = ! empty( $icon_class ) ? $icon_class : 'tutor-icon-exception4'; +$show_rating_average = isset( $show_rating_average ) ? (bool) $show_rating_average : false; +?> +
+ + = $i; + $is_half = ! $is_full && ( $rating >= ( $i - 0.5 ) ); + + $icon_name = $is_full + ? Icon::STAR + : ( $is_half ? Icon::STAR_LINE : Icon::STAR_LINE ); // Todo: Half star icon. + + $icon_html = tutor_utils()->render_svg_icon( $icon_name, 16, 16, array(), true ); // phpcs:ignore + ?> +
+ +
+ + +
+ +
+ +
diff --git a/templates/dashboard/instructor/analytics/stat-card.php b/templates/dashboard/instructor/analytics/stat-card.php new file mode 100644 index 0000000000..1e99250551 --- /dev/null +++ b/templates/dashboard/instructor/analytics/stat-card.php @@ -0,0 +1,55 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +// Default values. +$icon_size = $icon_size ?? 24; +$variation = isset( $variation ) ? $variation : 'enrolled'; +$value = isset( $value ) ? $value : 0; +$change = isset( $change ) ? $change : ''; +$show_graph = isset( $show_graph ) ? $show_graph : false; +$data = isset( $data ) ? $data : array( 0, 0, 0 ); + +// Required fields validation. +if ( ! isset( $card_title ) || empty( $card_title ) ) { + return; +} +if ( ! isset( $icon ) || empty( $icon ) ) { + return; +} + +$change_display = ! empty( $change ) ? $change : ''; + +?> +
+
+
+ +
+
+ render_svg_icon( $icon, $icon_size, $icon_size ); ?> +
+
+
+
+ +
+ +

+ +

+ +
+ +
+ +
+ +
From 5f3927892aa8748b8b41883d97583267ed75cf6d Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 26 Dec 2025 18:01:51 +0600 Subject: [PATCH 020/109] modal and chart --- .../scss/frontend/dashboard/_stat-card.scss | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/assets/src/scss/frontend/dashboard/_stat-card.scss b/assets/src/scss/frontend/dashboard/_stat-card.scss index 874fe50a4a..5255140cb5 100644 --- a/assets/src/scss/frontend/dashboard/_stat-card.scss +++ b/assets/src/scss/frontend/dashboard/_stat-card.scss @@ -58,6 +58,12 @@ margin-top: 38px; } + &-value { + @include tutor-text('h3'); + } + } + } + &-time-spent { .tutor-stat-card-icon { color: $tutor-text-exception4; @@ -69,9 +75,9 @@ } &-courses { - .tutor-stat-card-icon { - color: $tutor-text-success; - } + .tutor-stat-card-icon { + color: $tutor-text-success; + } .tutor-stat-card-value { color: $tutor-text-success; @@ -80,9 +86,9 @@ &-students { - .tutor-stat-card-icon { - color: $tutor-text-brand; - } + .tutor-stat-card-icon { + color: $tutor-text-brand; + } .tutor-stat-card-value { color: $tutor-text-brand; @@ -90,9 +96,9 @@ } &-reviews { - .tutor-stat-card-icon { - color: $tutor-warning-400; - } + .tutor-stat-card-icon { + color: $tutor-warning-400; + } .tutor-stat-card-value { color: $tutor-warning-400; @@ -100,9 +106,9 @@ } &-passed { - .tutor-stat-card-icon { - color: $tutor-text-brand; - } + .tutor-stat-card-icon { + color: $tutor-text-brand; + } .tutor-stat-card-value { color: $tutor-text-brand; @@ -110,9 +116,9 @@ } &-progress { - .tutor-stat-card-icon { - color: $tutor-exception-5; - } + .tutor-stat-card-icon { + color: $tutor-exception-5; + } .tutor-stat-card-value { color: $tutor-exception-5; @@ -120,9 +126,9 @@ } &-completed { - .tutor-stat-card-icon { - color: $tutor-text-success; - } + .tutor-stat-card-icon { + color: $tutor-text-success; + } .tutor-stat-card-value { color: $tutor-text-success; @@ -130,9 +136,9 @@ } &-qa { - .tutor-stat-card-icon { - color: $tutor-exception-2; - } + .tutor-stat-card-icon { + color: $tutor-exception-2; + } .tutor-stat-card-value { color: $tutor-exception-2; @@ -140,19 +146,14 @@ } &-book-2 { - .tutor-stat-card-icon { - color: $tutor-exception-1; - } + .tutor-stat-card-icon { + color: $tutor-exception-1; + } .tutor-stat-card-value { color: $tutor-exception-1; } } - &-value { - @include tutor-text('h3'); - } - } - } &-exception1 { .tutor-stat-card-icon { From cb0d2299978dcc9a66b2346e0ae860ac8ba410a8 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 29 Dec 2025 17:58:23 +0600 Subject: [PATCH 021/109] modal --- .../learning-area/layout/_sidebar.scss | 128 ------------------ components/BaseComponent.php | 6 +- 2 files changed, 5 insertions(+), 129 deletions(-) diff --git a/assets/src/scss/frontend/learning-area/layout/_sidebar.scss b/assets/src/scss/frontend/learning-area/layout/_sidebar.scss index 1785172cc9..1770ea796a 100644 --- a/assets/src/scss/frontend/learning-area/layout/_sidebar.scss +++ b/assets/src/scss/frontend/learning-area/layout/_sidebar.scss @@ -80,132 +80,4 @@ } } - .tutor-learning-nav { - @include tutor-flex(column); - gap: $tutor-spacing-1; - padding: $tutor-spacing-6 $tutor-spacing-4; - max-height: 440px; - overflow-y: auto; - - &-topic { - @include tutor-flex(column); - gap: $tutor-spacing-1; - - .tutor-learning-nav-header { - @include tutor-flex(row, center); - gap: $tutor-spacing-4; - padding-block: $tutor-spacing-4; - padding-inline-start: $tutor-spacing-5; - padding-inline-end: $tutor-spacing-3; - cursor: pointer; - - &-progress { - padding: $tutor-spacing-1; - - &-inner { - height: 16px; - width: 16px; - border: 1px solid $tutor-border-hover; - border-radius: 100%; - background-color: $tutor-actions-gray-empty; - } - } - - &-title { - @include tutor-typography(small, medium, subdued); - } - - &-arrow { - @include tutor-flex-center(); - margin-inline-start: auto; - color: $tutor-icon-secondary; - transition: transform 0.25s ease-in-out; - - &.is-expanded { - transform: rotate(180deg); - } - } - } - - &.active { - .tutor-learning-nav-header-title { - color: $tutor-text-primary; - } - } - } - - .tutor-learning-nav-body { - @include tutor-flex(column); - gap: $tutor-spacing-1; - padding-inline-start: 30px; - position: relative; - - &::before { - content: ''; - height: calc(100% - 20px); - width: 1.5px; - position: absolute; - top: 0; - left: 21px; - background-color: $tutor-surface-l1-hover; - } - } - - .tutor-learning-nav-item { - position: relative; - - a { - @include tutor-button-reset(); - @include tutor-typography(small, medium, subdued); - @include tutor-flex(row, center); - gap: $tutor-spacing-4; - padding: $tutor-spacing-4 $tutor-spacing-5; - min-height: 40px; - - svg { - color: $tutor-icon-subdued; - } - } - - &.active { - a { - color: $tutor-text-primary; - } - - &::before { - content: ''; - height: 30px; - width: 2px; - background-color: $tutor-actions-brand-primary; - position: absolute; - top: 5px; - left: -9px; - } - } - } - } - - .tutor-learning-pages { - @include tutor-flex(column); - gap: $tutor-spacing-1; - - &-item { - @include tutor-button-reset(); - @include tutor-typography(small); - @include tutor-flex(row, center); - gap: $tutor-spacing-5; - padding: $tutor-spacing-4 $tutor-spacing-5; - border-radius: $tutor-radius-md; - transition: background-color 0.25s ease-in-out; - - &:hover { - background-color: $tutor-tab-sidebar-l2-hover; - } - - &.active { - background-color: $tutor-tab-sidebar-l2-active; - color: $tutor-text-brand; - } - } - } } diff --git a/components/BaseComponent.php b/components/BaseComponent.php index 7cc7f57a0c..7641c9455b 100644 --- a/components/BaseComponent.php +++ b/components/BaseComponent.php @@ -112,6 +112,11 @@ protected function render_attributes(): string { * @return string Escaped string. */ protected function esc( $value, $esc_fn = 'esc_html' ): string { + + if ( 'no_esc' === $esc_fn ) { + return $value; + } + return call_user_func( $esc_fn, $value ); } @@ -138,5 +143,4 @@ public function render(): void { // phpcs:ignore -- Sanitization is performed within each child class’s `get` method implementation. echo $this->get(); } - } From 3da7b8e98eaaf1fa7415a82bf1896a278f036087 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 30 Dec 2025 12:41:51 +0600 Subject: [PATCH 022/109] undo previous commit --- components/InputField.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/components/InputField.php b/components/InputField.php index 9de14e112a..d2787bfbd3 100644 --- a/components/InputField.php +++ b/components/InputField.php @@ -902,13 +902,7 @@ class="tutor-select-option" :data-disabled="option.disabled ? \'true\' : \'false\'" :data-selected="isSelected(option) ? \'true\' : \'false\'" :data-highlighted="isHighlighted(filteredOptions.indexOf(option)) ? \'true\' : \'false\'" - @click=" - selectOption(option); - - if (option.href) { - window.location.href = option.href; - } - " + @click="selectOption(option)" @mouseenter="highlightedIndex = filteredOptions.indexOf(option)" role="option" :aria-selected="isSelected(option).toString()" From 5e8cf9f672bbb94f8754e7c70725465dc4256ce0 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 30 Dec 2025 16:43:21 +0600 Subject: [PATCH 023/109] topic details --- classes/Utils.php | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/classes/Utils.php b/classes/Utils.php index 1bd96a1b97..b734532858 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10782,4 +10782,66 @@ public function get_stepper_sort_options() { ), ); } + + public function get_topic_progress_by_course_id( $course_id ) { + + $topics = $this->get_topics( $course_id ); + $topic_list = array(); + + if ( $topics->have_posts() ) { + while ( $topics->have_posts() ) { + $topics->the_post(); + $topic_list[] = array( + 'topic_id' => get_the_ID(), + 'topic_summery' => get_the_content(), + 'topic_title' => the_title(), + 'lessons' => tutor_utils()->get_course_contents_by_topic( get_the_ID(), -1 ) + ); + + if ( $lessons->have_posts() ) { + foreach ( $lessons->posts as $post ) { + if ( 'tutor_quiz' === $post->post_type ) { + $quiz = $post; + $topic_list[]['lessons']['tutor_quiz'] = array( + 'quiz_id' => $quiz->ID, + 'quiz_link' => esc_url( get_permalink( $quiz->ID ) ), + 'quiz_title' => $quiz->post_title, + 'has_attempt' => tutor_utils()->has_attempted_quiz( $student_id, $quiz->ID ), + 'time_limit' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_value' ), + 'time_type' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_type' ), + ) + } elseif ( 'tutor_assignments' === $post->post_type ) { + $topic_list[]['lessons']['tutor_assignments'] = array( + 'assignment_submitted' => tutor_utils()->get_submitted_assignment_count( $post->ID, $student_id ), + 'assignment_id' => $post->ID, + 'assignment_link' => esc_url( get_permalink( $post->ID ) ), + 'assignment_title' => $post->post_title + ); + } elseif ( 'tutor_zoom_meeting' === $post->post_type ) { + $topic_list[]['lessons']['tutor_zoom_meeting'] = array( + 'zoom_meeting' => $post->ID, + 'zoom_meeting_link' => esc_url( get_permalink( $post->ID ) ) + ) + } else { + + $topic_list[]['lessons']['lesson'] = array ( + 'video' => tutor_utils()->get_video_info( $post->ID ), + 'video_play_time' => $video->playtime ?? '', + 'is_completed_lesson' => tutor_utils()->is_completed_lesson( $post->ID, $student_id ), + 'lesson_id' => $post->ID, + 'lesson_link' => esc_url( get_permalink( $post->ID ) ), + 'lesson_title' => $post->post_title + ); + } + } + + $lessons->reset_postdata(); + + } + + } + $topics->reset_postdata(); + wp_reset_postdata(); + } + } } From 36ec947ad8b49cc93d74a5e333a9c12a46d04ffe Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 30 Dec 2025 16:43:58 +0600 Subject: [PATCH 024/109] topic details code format --- classes/Utils.php | 49 +++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index b734532858..f24062b29c 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10792,54 +10792,53 @@ public function get_topic_progress_by_course_id( $course_id ) { while ( $topics->have_posts() ) { $topics->the_post(); $topic_list[] = array( - 'topic_id' => get_the_ID(), - 'topic_summery' => get_the_content(), - 'topic_title' => the_title(), - 'lessons' => tutor_utils()->get_course_contents_by_topic( get_the_ID(), -1 ) + 'topic_id' => get_the_ID(), + 'topic_summery' => get_the_content(), + 'topic_title' => the_title(), + 'lessons' => tutor_utils()->get_course_contents_by_topic( get_the_ID(), -1 ), ); if ( $lessons->have_posts() ) { foreach ( $lessons->posts as $post ) { if ( 'tutor_quiz' === $post->post_type ) { - $quiz = $post; + $quiz = $post; $topic_list[]['lessons']['tutor_quiz'] = array( - 'quiz_id' => $quiz->ID, - 'quiz_link' => esc_url( get_permalink( $quiz->ID ) ), - 'quiz_title' => $quiz->post_title, + 'quiz_id' => $quiz->ID, + 'quiz_link' => esc_url( get_permalink( $quiz->ID ) ), + 'quiz_title' => $quiz->post_title, 'has_attempt' => tutor_utils()->has_attempted_quiz( $student_id, $quiz->ID ), - 'time_limit' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_value' ), - 'time_type' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_type' ), + 'time_limit' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_value' ), + 'time_type' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_type' ), ) } elseif ( 'tutor_assignments' === $post->post_type ) { $topic_list[]['lessons']['tutor_assignments'] = array( 'assignment_submitted' => tutor_utils()->get_submitted_assignment_count( $post->ID, $student_id ), - 'assignment_id' => $post->ID, - 'assignment_link' => esc_url( get_permalink( $post->ID ) ), - 'assignment_title' => $post->post_title + 'assignment_id' => $post->ID, + 'assignment_link' => esc_url( get_permalink( $post->ID ) ), + 'assignment_title' => $post->post_title, ); } elseif ( 'tutor_zoom_meeting' === $post->post_type ) { $topic_list[]['lessons']['tutor_zoom_meeting'] = array( - 'zoom_meeting' => $post->ID, - 'zoom_meeting_link' => esc_url( get_permalink( $post->ID ) ) + 'zoom_meeting' => $post->ID, + 'zoom_meeting_link' => esc_url( get_permalink( $post->ID ) ), ) } else { - - $topic_list[]['lessons']['lesson'] = array ( - 'video' => tutor_utils()->get_video_info( $post->ID ), - 'video_play_time' => $video->playtime ?? '', + + $topic_list[]['lessons']['lesson'] = array( + 'video' => tutor_utils()->get_video_info( $post->ID ), + 'video_play_time' => $video->playtime ?? '', 'is_completed_lesson' => tutor_utils()->is_completed_lesson( $post->ID, $student_id ), - 'lesson_id' => $post->ID, - 'lesson_link' => esc_url( get_permalink( $post->ID ) ), - 'lesson_title' => $post->post_title + 'lesson_id' => $post->ID, + 'lesson_link' => esc_url( get_permalink( $post->ID ) ), + 'lesson_title' => $post->post_title, ); } } $lessons->reset_postdata(); - - } - } + } + } $topics->reset_postdata(); wp_reset_postdata(); } From 638e0fdaf0fb2ba26f217940cd4c126e0f59730e Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 30 Dec 2025 18:01:42 +0600 Subject: [PATCH 025/109] earnings --- assets/icons/commission.svg | 5 ++ assets/icons/fees.svg | 5 ++ assets/icons/sale.svg | 5 ++ assets/icons/wallet.svg | 5 ++ assets/icons/withdraw.svg | 6 ++ assets/src/js/v3/shared/icons/types.ts | 4 +- .../scss/frontend/dashboard/_stat-card.scss | 60 +++++++++++++++++++ classes/Icon.php | 7 ++- classes/Utils.php | 4 +- 9 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 assets/icons/commission.svg create mode 100644 assets/icons/fees.svg create mode 100644 assets/icons/sale.svg create mode 100644 assets/icons/wallet.svg create mode 100644 assets/icons/withdraw.svg diff --git a/assets/icons/commission.svg b/assets/icons/commission.svg new file mode 100644 index 0000000000..1b3fb72e59 --- /dev/null +++ b/assets/icons/commission.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/fees.svg b/assets/icons/fees.svg new file mode 100644 index 0000000000..2a329397dd --- /dev/null +++ b/assets/icons/fees.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/sale.svg b/assets/icons/sale.svg new file mode 100644 index 0000000000..0d5c91cfec --- /dev/null +++ b/assets/icons/sale.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/wallet.svg b/assets/icons/wallet.svg new file mode 100644 index 0000000000..9d559af3f3 --- /dev/null +++ b/assets/icons/wallet.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/icons/withdraw.svg b/assets/icons/withdraw.svg new file mode 100644 index 0000000000..d734eeee9d --- /dev/null +++ b/assets/icons/withdraw.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/src/js/v3/shared/icons/types.ts b/assets/src/js/v3/shared/icons/types.ts index 21c30e9bc2..b20d4787f8 100644 --- a/assets/src/js/v3/shared/icons/types.ts +++ b/assets/src/js/v3/shared/icons/types.ts @@ -320,8 +320,8 @@ export const icons = [ 'spinner', 'spreadsheet', 'star', - 'starLine', 'star2', + 'starLine', 'stepper', 'stopwatch', 'storeEye', @@ -362,10 +362,12 @@ export const icons = [ 'videoQuality', 'vimeo', 'visited', + 'wallet', 'warning', 'weightBox', 'wifi', 'wishlist', + 'withdraw', 'x', 'xls', 'xml', diff --git a/assets/src/scss/frontend/dashboard/_stat-card.scss b/assets/src/scss/frontend/dashboard/_stat-card.scss index 5255140cb5..6da2611a39 100644 --- a/assets/src/scss/frontend/dashboard/_stat-card.scss +++ b/assets/src/scss/frontend/dashboard/_stat-card.scss @@ -194,4 +194,64 @@ color: $tutor-text-exception5; } } + + &-earning { + .tutor-stat-card-icon { + color: $tutor-text-success; + } + + .tutor-stat-card-value { + color: $tutor-text-success; + } + } + + &-wallet { + .tutor-stat-card-icon { + color: $tutor-text-brand; + } + + .tutor-stat-card-value { + color: $tutor-text-brand; + } + } + + &-withdraw { + .tutor-stat-card-icon { + color: $tutor-text-exception5; + } + + .tutor-stat-card-value { + color: $tutor-text-exception5; + } + } + + &-sale { + .tutor-stat-card-icon { + color: $tutor-text-exception1; + } + + .tutor-stat-card-value { + color: $tutor-text-exception1; + } + } + + &-commission { + .tutor-stat-card-icon { + color: $tutor-text-exception2; + } + + .tutor-stat-card-value { + color: $tutor-text-exception2; + } + } + + &-fees { + .tutor-stat-card-icon { + color: $tutor-text-exception4; + } + + .tutor-stat-card-value { + color: $tutor-text-exception4; + } + } } diff --git a/classes/Icon.php b/classes/Icon.php index 11c60b6913..dbc53e55d7 100644 --- a/classes/Icon.php +++ b/classes/Icon.php @@ -90,6 +90,7 @@ final class Icon { const CODING = 'coding'; const COLLAPSED = 'collapsed'; const COLOR_OPTION = 'color-option'; + const COMMISSION = 'commission'; const COMMAND = 'command'; const COMMENTS = 'comments'; const COMPLETED = 'completed'; @@ -157,6 +158,7 @@ final class Icon { const EYE = 'eye'; const EYE_LINE = 'eye-line'; const FACEBOOK = 'facebook'; + const FEES = 'fees'; const FEATHER = 'feather'; const FILE = 'file'; const FILE_ATTACHEMENT = 'file-attachement'; @@ -307,6 +309,7 @@ final class Icon { const RESOURCES = 'resources'; const ROTATE = 'rotate'; const RTF = 'rtf'; + const SALE = 'sale'; const SALE_TYPE = 'sale-type'; const SAVE = 'save'; const SEARCH = 'search'; @@ -336,8 +339,8 @@ final class Icon { const SPINNER = 'spinner'; const SPREADSHEET = 'spreadsheet'; const STAR = 'star'; - const STAR_LINE = 'star-line'; const STAR_2 = 'star-2'; + const STAR_LINE = 'star-line'; const STEPPER = 'stepper'; const STOPWATCH = 'stopwatch'; const STORE_EYE = 'store-eye'; @@ -378,10 +381,12 @@ final class Icon { const VIDEO_QUALITY = 'video-quality'; const VIMEO = 'vimeo'; const VISITED = 'visited'; + const WALLET = 'wallet'; const WARNING = 'warning'; const WEIGHT_BOX = 'weight-box'; const WIFI = 'wifi'; const WISHLIST = 'wishlist'; + const WITHDRAW = 'withdraw'; const X = 'x'; const XLS = 'xls'; const XML = 'xml'; diff --git a/classes/Utils.php b/classes/Utils.php index f24062b29c..95acff790e 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10809,7 +10809,7 @@ public function get_topic_progress_by_course_id( $course_id ) { 'has_attempt' => tutor_utils()->has_attempted_quiz( $student_id, $quiz->ID ), 'time_limit' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_value' ), 'time_type' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_type' ), - ) + ); } elseif ( 'tutor_assignments' === $post->post_type ) { $topic_list[]['lessons']['tutor_assignments'] = array( 'assignment_submitted' => tutor_utils()->get_submitted_assignment_count( $post->ID, $student_id ), @@ -10821,7 +10821,7 @@ public function get_topic_progress_by_course_id( $course_id ) { $topic_list[]['lessons']['tutor_zoom_meeting'] = array( 'zoom_meeting' => $post->ID, 'zoom_meeting_link' => esc_url( get_permalink( $post->ID ) ), - ) + ); } else { $topic_list[]['lessons']['lesson'] = array( From be12d38390b71950f6b40a3d9fed680772f22bc6 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 1 Jan 2026 15:31:53 +0600 Subject: [PATCH 026/109] Topic list for course progress --- classes/Utils.php | 149 ++++++++++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 50 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 95acff790e..a75bb67c1d 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10746,6 +10746,21 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U return null; } + /** + * Render a template and return its output as a string. + * + * @since 4.0.0 + * + * @param string $template Template file path or slug. + * @param array $data Data to be passed to the template. + * @param string $type Template type. Allowed values: + * - include + * - custom_template + * - template + * @param bool $tutor_pro Whether to load Tutor Pro template. + * + * @return string Rendered template output. + */ public function render_template( $template, $data, $type, $tutor_pro = false ) { ob_start(); @@ -10770,6 +10785,13 @@ public function render_template( $template, $data, $type, $tutor_pro = false ) { return (string) ob_get_clean(); } + /** + * Get stepper sort options. + * + * @since 4.0.0 + * + * @return array[] List of sort options with id and label. + */ public function get_stepper_sort_options() { return array( array( @@ -10783,64 +10805,91 @@ public function get_stepper_sort_options() { ); } - public function get_topic_progress_by_course_id( $course_id ) { + /** + * Get topic-wise progress data for a course and a student. + * + * @since 4.0.0 + * + * @param int $course_id Course ID. + * @param int $student_id Student ID. + * + * @return array[] Topic progress data. + */ + public function get_topic_progress_by_course_id( $course_id, $student_id ) { + + $topics_query = $this->get_topics( $course_id ); - $topics = $this->get_topics( $course_id ); $topic_list = array(); - if ( $topics->have_posts() ) { - while ( $topics->have_posts() ) { - $topics->the_post(); - $topic_list[] = array( - 'topic_id' => get_the_ID(), - 'topic_summery' => get_the_content(), - 'topic_title' => the_title(), - 'lessons' => tutor_utils()->get_course_contents_by_topic( get_the_ID(), -1 ), - ); + if ( empty( $topics_query ) || ! $topics_query->have_posts() ) { + return $topic_list; + } - if ( $lessons->have_posts() ) { - foreach ( $lessons->posts as $post ) { - if ( 'tutor_quiz' === $post->post_type ) { - $quiz = $post; - $topic_list[]['lessons']['tutor_quiz'] = array( - 'quiz_id' => $quiz->ID, - 'quiz_link' => esc_url( get_permalink( $quiz->ID ) ), - 'quiz_title' => $quiz->post_title, - 'has_attempt' => tutor_utils()->has_attempted_quiz( $student_id, $quiz->ID ), - 'time_limit' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_value' ), - 'time_type' => tutor_utils()->get_quiz_option( $quiz->ID, 'time_limit.time_type' ), - ); - } elseif ( 'tutor_assignments' === $post->post_type ) { - $topic_list[]['lessons']['tutor_assignments'] = array( - 'assignment_submitted' => tutor_utils()->get_submitted_assignment_count( $post->ID, $student_id ), - 'assignment_id' => $post->ID, - 'assignment_link' => esc_url( get_permalink( $post->ID ) ), - 'assignment_title' => $post->post_title, - ); - } elseif ( 'tutor_zoom_meeting' === $post->post_type ) { - $topic_list[]['lessons']['tutor_zoom_meeting'] = array( - 'zoom_meeting' => $post->ID, - 'zoom_meeting_link' => esc_url( get_permalink( $post->ID ) ), - ); - } else { - - $topic_list[]['lessons']['lesson'] = array( - 'video' => tutor_utils()->get_video_info( $post->ID ), - 'video_play_time' => $video->playtime ?? '', - 'is_completed_lesson' => tutor_utils()->is_completed_lesson( $post->ID, $student_id ), - 'lesson_id' => $post->ID, - 'lesson_link' => esc_url( get_permalink( $post->ID ) ), - 'lesson_title' => $post->post_title, - ); - } - } + foreach ( $topics_query->posts as $topic_post ) { + $topic_id = (int) $topic_post->ID; + + $topic = array( + 'topic_id' => $topic_id, + 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), + 'topic_title' => get_the_title( $topic_id ), + 'items' => array(), + ); + + $contents_query = tutor_utils()->get_course_contents_by_topic( $topic_id, -1 ); + + if ( ! empty( $contents_query ) && $contents_query->have_posts() ) { + foreach ( $contents_query->posts as $content_post ) { + $post_id = (int) $content_post->ID; + $post_type = $content_post->post_type; + + if ( 'tutor_quiz' === $post_type ) { + $topic['items'][] = array( + 'type' => 'quiz', + 'quiz_id' => $post_id, + 'quiz_link' => esc_url( get_permalink( $post_id ) ), + 'quiz_title' => $content_post->post_title, + 'has_attempt' => (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ), + 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), + 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), + ); - $lessons->reset_postdata(); + } elseif ( 'tutor_assignments' === $post_type ) { + $topic['items'][] = array( + 'type' => 'assignment', + 'assignment_id' => $post_id, + 'assignment_link' => esc_url( get_permalink( $post_id ) ), + 'assignment_title' => $content_post->post_title, + 'assignment_submitted' => (int) tutor_utils()->get_submitted_assignment_count( $post_id, $student_id ), + ); + + } elseif ( 'tutor_zoom_meeting' === $post_type ) { + $topic['items'][] = array( + 'type' => 'zoom_meeting', + 'zoom_meeting_id' => $post_id, + 'zoom_meeting_link' => esc_url( get_permalink( $post_id ) ), + ); + } else { + $video = tutor_utils()->get_video_info( $post_id ); + + $topic['items'][] = array( + 'type' => 'lesson', + 'lesson_id' => $post_id, + 'lesson_link' => esc_url( get_permalink( $post_id ) ), + 'lesson_title' => $content_post->post_title, + 'video' => $video, + 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', + 'is_completed_lesson' => (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ), + ); + } } } - $topics->reset_postdata(); - wp_reset_postdata(); + + $topic_list[] = $topic; } + + wp_reset_postdata(); + + return $topic_list; } } From fcfe4d926cf0df510c0251ec2019145c0d2e3b09 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Mon, 5 Jan 2026 04:16:08 +0600 Subject: [PATCH 027/109] student progress modal --- classes/Utils.php | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index a4d9f8d5d0..ee9c57bec9 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10901,6 +10901,7 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), 'topic_title' => get_the_title( $topic_id ), 'items' => array(), + 'topic_completed' => true, ); $contents_query = tutor_utils()->get_course_contents_by_topic( $topic_id, -1 ); @@ -10909,25 +10910,35 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { foreach ( $contents_query->posts as $content_post ) { $post_id = (int) $content_post->ID; $post_type = $content_post->post_type; + $is_completed = true; if ( 'tutor_quiz' === $post_type ) { + + $has_attempt = (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ); + $is_completed = $has_attempt; + $topic['items'][] = array( 'type' => 'quiz', 'quiz_id' => $post_id, 'quiz_link' => esc_url( get_permalink( $post_id ) ), 'quiz_title' => $content_post->post_title, - 'has_attempt' => (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ), + 'has_attempt' => $has_attempt, 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), ); } elseif ( 'tutor_assignments' === $post_type ) { + + $submitted_count = (int) tutor_utils()->get_submitted_assignment_count( $post_id, $student_id ); + $is_completed = $submitted_count > 0; + $topic['items'][] = array( 'type' => 'assignment', 'assignment_id' => $post_id, 'assignment_link' => esc_url( get_permalink( $post_id ) ), 'assignment_title' => $content_post->post_title, - 'assignment_submitted' => (int) tutor_utils()->get_submitted_assignment_count( $post_id, $student_id ), + 'assignment_submitted' => $submitted_count, + 'is_completed' => $is_completed, ); } elseif ( 'tutor_zoom_meeting' === $post_type ) { @@ -10939,6 +10950,8 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { } else { $video = tutor_utils()->get_video_info( $post_id ); + $is_completed_lesson = (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ); + $is_completed = $is_completed_lesson; $topic['items'][] = array( 'type' => 'lesson', @@ -10947,9 +10960,13 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { 'lesson_title' => $content_post->post_title, 'video' => $video, 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', - 'is_completed_lesson' => (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ), + 'is_completed_lesson' => $is_completed_lesson ); } + + if ( ! $is_completed ) { + $topic['topic_completed'] = false; + } } } From 23ddcd6347af90192b5598158fcec7b6dedc0520 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 6 Jan 2026 12:25:00 +0600 Subject: [PATCH 028/109] course progress design updated --- classes/Utils.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 43b78c8023..0c977b1174 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10907,19 +10907,20 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { $topic_id = (int) $topic_post->ID; $topic = array( - 'topic_id' => $topic_id, - 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), - 'topic_title' => get_the_title( $topic_id ), - 'items' => array(), + 'topic_id' => $topic_id, + 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), + 'topic_title' => get_the_title( $topic_id ), + 'items' => array(), 'topic_completed' => true, + 'topic_started' => false, ); $contents_query = tutor_utils()->get_course_contents_by_topic( $topic_id, -1 ); if ( ! empty( $contents_query ) && $contents_query->have_posts() ) { foreach ( $contents_query->posts as $content_post ) { - $post_id = (int) $content_post->ID; - $post_type = $content_post->post_type; + $post_id = (int) $content_post->ID; + $post_type = $content_post->post_type; $is_completed = true; if ( 'tutor_quiz' === $post_type ) { @@ -10976,6 +10977,8 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { if ( ! $is_completed ) { $topic['topic_completed'] = false; + } else { + $topic['topic_started'] = true; } } } From 68d211470e2bd4c0ec9d15a9b01aedbd3eb13a87 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 6 Jan 2026 12:54:12 +0600 Subject: [PATCH 029/109] course progress condition update --- classes/Utils.php | 69 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 0c977b1174..a64248d06b 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -9544,7 +9544,7 @@ public function should_show_dicussion_menu(): bool { */ public function default_menus(): array { $items = array( - 'index' => array( + 'index' => array( 'title' => __( 'Home', 'tutor' ), 'icon' => Icon::HOME, ), @@ -10826,7 +10826,7 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U /** * Render a template and return its output as a string. - * + * * @since 4.0.0 * * @param string $template Template file path or slug. @@ -10865,7 +10865,7 @@ public function render_template( $template, $data, $type, $tutor_pro = false ) { /** * Get stepper sort options. - * + * * @since 4.0.0 * * @return array[] List of sort options with id and label. @@ -10887,7 +10887,7 @@ public function get_stepper_sort_options() { * Get topic-wise progress data for a course and a student. * * @since 4.0.0 - * + * * @param int $course_id Course ID. * @param int $student_id Student ID. * @@ -10911,7 +10911,7 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), 'topic_title' => get_the_title( $topic_id ), 'items' => array(), - 'topic_completed' => true, + 'topic_completed' => true, 'topic_started' => false, ); @@ -10919,23 +10919,22 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { if ( ! empty( $contents_query ) && $contents_query->have_posts() ) { foreach ( $contents_query->posts as $content_post ) { - $post_id = (int) $content_post->ID; - $post_type = $content_post->post_type; - $is_completed = true; + $post_id = (int) $content_post->ID; + $post_type = $content_post->post_type; + $is_completed = true; if ( 'tutor_quiz' === $post_type ) { - $has_attempt = (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ); - $is_completed = $has_attempt; + $is_completed = (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ); $topic['items'][] = array( - 'type' => 'quiz', - 'quiz_id' => $post_id, - 'quiz_link' => esc_url( get_permalink( $post_id ) ), - 'quiz_title' => $content_post->post_title, - 'has_attempt' => $has_attempt, - 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), - 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), + 'type' => 'quiz', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), + 'title' => $content_post->post_title, + 'is_completed' => $is_completed, + 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), + 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), ); } elseif ( 'tutor_assignments' === $post_type ) { @@ -10944,34 +10943,32 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { $is_completed = $submitted_count > 0; $topic['items'][] = array( - 'type' => 'assignment', - 'assignment_id' => $post_id, - 'assignment_link' => esc_url( get_permalink( $post_id ) ), - 'assignment_title' => $content_post->post_title, - 'assignment_submitted' => $submitted_count, - 'is_completed' => $is_completed, + 'type' => 'assignment', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), + 'title' => $content_post->post_title, + 'is_completed' => $is_completed, ); } elseif ( 'tutor_zoom_meeting' === $post_type ) { $topic['items'][] = array( - 'type' => 'zoom_meeting', - 'zoom_meeting_id' => $post_id, - 'zoom_meeting_link' => esc_url( get_permalink( $post_id ) ), + 'type' => 'zoom_meeting', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), ); } else { - $video = tutor_utils()->get_video_info( $post_id ); - $is_completed_lesson = (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ); - $is_completed = $is_completed_lesson; + $video = tutor_utils()->get_video_info( $post_id ); + $is_completed = (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ); $topic['items'][] = array( - 'type' => 'lesson', - 'lesson_id' => $post_id, - 'lesson_link' => esc_url( get_permalink( $post_id ) ), - 'lesson_title' => $content_post->post_title, - 'video' => $video, - 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', - 'is_completed_lesson' => $is_completed_lesson + 'type' => 'lesson', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), + 'title' => $content_post->post_title, + 'video' => $video, + 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', + 'is_completed' => $is_completed, ); } From 380f2f2ce1d055f8bb4e1aea275e3993a955b81a Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 6 Jan 2026 12:58:08 +0600 Subject: [PATCH 030/109] comment added. --- classes/Utils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Utils.php b/classes/Utils.php index a64248d06b..e1d14dbf5d 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10950,7 +10950,7 @@ public function get_topic_progress_by_course_id( $course_id, $student_id ) { 'is_completed' => $is_completed, ); - } elseif ( 'tutor_zoom_meeting' === $post_type ) { + } elseif ( 'tutor_zoom_meeting' === $post_type ) { //@todo Need to add more information. $topic['items'][] = array( 'type' => 'zoom_meeting', 'id' => $post_id, From fff88de40f426b3bcbb1fe3c645f5757801f6dc5 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 7 Jan 2026 15:14:45 +0600 Subject: [PATCH 031/109] Removed unnecessary data. --- classes/Utils.php | 20 ------- components/InputField.php | 110 -------------------------------------- 2 files changed, 130 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index e1d14dbf5d..381ce42164 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10863,26 +10863,6 @@ public function render_template( $template, $data, $type, $tutor_pro = false ) { return (string) ob_get_clean(); } - /** - * Get stepper sort options. - * - * @since 4.0.0 - * - * @return array[] List of sort options with id and label. - */ - public function get_stepper_sort_options() { - return array( - array( - 'id' => 'newest', - 'label' => __( 'Newest First', 'tutor' ), - ), - array( - 'id' => 'oldest', - 'label' => __( 'Oldest First', 'tutor' ), - ), - ); - } - /** * Get topic-wise progress data for a course and a student. * diff --git a/components/InputField.php b/components/InputField.php index 4436a67aa9..1f3ab350e0 100644 --- a/components/InputField.php +++ b/components/InputField.php @@ -1604,9 +1604,6 @@ public function get(): string { case InputType::SWITCH: $input_html = $this->render_switch(); break; - case InputType::SEARCH: - $input_html = $this->render_search(); - break; case InputType::DATE: case InputType::DATE_TIME: $input_html = $this->render_date_input(); @@ -1643,111 +1640,4 @@ public function get(): string { return $this->component_string; } - - /** - * Render the HTML markup for a search-style input field. - * - * @since 4.0.0 - * - * Behavior: - * - Determines the input `id` from `$this->id` or falls back to `$this->name`. - * - Composes CSS classes based on: - * - `$this->left_icon` → adds `tutor-input-content-left` - * - `$this->right_icon` → adds `tutor-input-content-right` - * - `$this->clearable` → adds `tutor-input-content-clear` - * - Applies attributes produced by `$this->render_attributes()` and sets optional - * `placeholder` and `value` when provided. - * - When `$this->clearable` is true and `tutor_utils()` exists, renders a “cross” SVG icon - * and outputs a clear button with: - * - `x-cloak` - * - `x-show="values.{name} && String(values.{name}).length > 0"` - * - `@click="setValue('{name}', '')"` - * - * @return string Fully composed HTML for the input, icons, and clear button. - */ - protected function render_search() { - $input_id = ! empty( $this->id ) ? $this->id : $this->name; - - $input_classes = 'tutor-input'; - if ( ! empty( $this->left_icon ) ) { - $input_classes .= ' tutor-input-content-left'; - } - if ( ! empty( $this->right_icon ) ) { - $input_classes .= ' tutor-input-content-right'; - } - if ( $this->clearable ) { - $input_classes .= ' tutor-input-content-clear'; - } - - $input_attrs = sprintf( - 'type="%s" id="%s" name="%s" class="%s" %s', - esc_attr( $this->type ), - esc_attr( $input_id ), - esc_attr( $this->name ), - esc_attr( $input_classes ), - $this->render_attributes() - ); - - if ( ! empty( $this->placeholder ) ) { - $input_attrs .= sprintf( ' placeholder="%s"', esc_attr( $this->placeholder ) ); - } - - if ( ! empty( $this->value ) ) { - $input_attrs .= sprintf( ' value="%s"', esc_attr( $this->value ) ); - } - - $left_icon_html = ''; - if ( ! empty( $this->left_icon ) ) { - $left_icon_html = sprintf( - '
%s
', - $this->left_icon - ); - } - - $right_icon_html = ''; - if ( ! empty( $this->right_icon ) ) { - $right_icon_html = sprintf( - '
%s
', - $this->right_icon - ); - } - - $clear_button_html = ''; - if ( $this->clearable ) { - $clear_icon = ''; - if ( function_exists( 'tutor_utils' ) ) { - ob_start(); - tutor_utils()->render_svg_icon( 'cross', 16, 16 ); - $clear_icon = ob_get_clean(); - } - - $clear_button_html = sprintf( - '', - esc_attr( $this->name ), - $clear_icon - ); - - } - - return sprintf( - '
- - %s - %s - %s -
- ', - $input_attrs, - $left_icon_html, - $right_icon_html, - $clear_button_html, - ); - } } From 027a9b893688934d36bb75817acdd99ba74f6f09 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 7 Jan 2026 16:50:55 +0600 Subject: [PATCH 032/109] statements filter --- models/OrderModel.php | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/models/OrderModel.php b/models/OrderModel.php index a77faad8b0..f68801c815 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -1902,17 +1902,20 @@ private static function should_active_pay_button( $order, $show_pay_button ) { * Retrieves statements for a specific user. * * @since 3.5.0 + * @since 4.0.0 Added $order_by and $order option. * - * @param string $post_type_in_clause SQL clause to filter the course post types. - * @param string $course_query SQL query string to further filter the courses . - * @param string $date_query SQL query string to filter by date range. - * @param int $user_id The user ID for which the statements are being retrieved. - * @param int $offset The offset for pagination. - * @param int $limit The number of rows to return. + * @param string $post_type_in_clause Prepared SQL IN clause containing allowed course post types. + * @param string $course_query Optional SQL fragment to filter by course ID. + * @param string $date_query Optional SQL fragment to filter by statement date. + * @param int $user_id User (instructor) ID. + * @param int $offset Number of records to skip (pagination offset). + * @param int $limit Maximum number of records to return. + * @param string $order_by Column name to order results by. + * @param string $order Sort direction. Accepts 'ASC' or 'DESC'. * * @return array */ - public function get_statements( $post_type_in_clause, $course_query, $date_query, $user_id, $offset, $limit ): array { + public function get_statements( $post_type_in_clause, $course_query, $date_query, $user_id, $offset, $limit, $order_by, $order ): array { global $wpdb; //phpcs:disable @@ -1936,9 +1939,8 @@ public function get_statements( $post_type_in_clause, $course_query, $date_query WHERE statements.user_id = %d {$course_query} {$date_query} - ORDER BY statements.created_at DESC - LIMIT %d, %d - ", + ORDER BY {$order_by} {$order} + LIMIT %d, %d", $user_id, $offset, $limit @@ -1953,8 +1955,7 @@ public function get_statements( $post_type_in_clause, $course_query, $date_query AND course.post_type IN ({$post_type_in_clause}) WHERE statements.user_id = %d {$course_query} - {$date_query} - ", + {$date_query}", $user_id ) ); From 7df5efaa0d9e4f4bcb68941cecbcdce985e6d33b Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 8 Jan 2026 11:43:46 +0600 Subject: [PATCH 033/109] student filter --- classes/Utils.php | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 381ce42164..4ab5ab9f84 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -3494,11 +3494,11 @@ public function get_students_by_instructor( int $instructor_id, int $offset, int $students = $wpdb->get_results( $wpdb->prepare( "SELECT COUNT(enrollment.post_author) AS course_taken, user.*, (SELECT post_date FROM {$wpdb->posts} WHERE post_author = user.ID LIMIT 1) AS enroll_date - FROM {$wpdb->posts} enrollment - INNER JOIN {$wpdb->posts} AS course - ON enrollment.post_parent=course.ID - INNER JOIN {$wpdb->users} AS user - ON user.ID = enrollment.post_author + FROM {$wpdb->posts} enrollment + INNER JOIN {$wpdb->posts} AS course + ON enrollment.post_parent=course.ID + INNER JOIN {$wpdb->users} AS user + ON user.ID = enrollment.post_author WHERE course.post_type = %s AND course.post_status IN ({$post_status}) AND enrollment.post_type = %s @@ -3507,11 +3507,9 @@ public function get_students_by_instructor( int $instructor_id, int $offset, int {$course_query} {$date_query} AND ( user.display_name LIKE %s OR user.user_nicename LIKE %s OR user.user_email = %s OR user.user_login LIKE %s ) - GROUP BY enrollment.post_author ORDER BY {$order_by} {$order} - LIMIT %d, %d - ", + LIMIT %d, %d", $course_post_type, 'tutor_enrolled', 'completed', @@ -3540,9 +3538,7 @@ public function get_students_by_instructor( int $instructor_id, int $offset, int {$course_query} {$date_query} GROUP BY enrollment.post_author - ORDER BY {$order_by} {$order} - - ", + ORDER BY {$order_by} {$order}", $course_post_type, 'tutor_enrolled', 'completed', From c014c64e6201a6c25f06ce0e498b6526cc2d3b10 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 8 Jan 2026 18:05:00 +0600 Subject: [PATCH 034/109] export analytics alpine. --- assets/src/js/v3/shared/utils/endpoints.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/src/js/v3/shared/utils/endpoints.ts b/assets/src/js/v3/shared/utils/endpoints.ts index c5f6b2bdc7..ced5fad1ba 100644 --- a/assets/src/js/v3/shared/utils/endpoints.ts +++ b/assets/src/js/v3/shared/utils/endpoints.ts @@ -162,6 +162,9 @@ const endpoints = { // Announcement CREATE_ANNOUNCEMENT: 'tutor_announcement_create', DELETE_ANNOUNCEMENT: 'tutor_announcement_delete', + + // Export Analytics + EXPORT_ANALYTICS: 'export_analytics', } as const; export default endpoints; From c304f1498e62559c401d47796dee0e66d4a94be0 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 9 Jan 2026 14:00:05 +0600 Subject: [PATCH 035/109] removed ts data for export --- assets/src/js/v3/shared/utils/endpoints.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/assets/src/js/v3/shared/utils/endpoints.ts b/assets/src/js/v3/shared/utils/endpoints.ts index ced5fad1ba..c5f6b2bdc7 100644 --- a/assets/src/js/v3/shared/utils/endpoints.ts +++ b/assets/src/js/v3/shared/utils/endpoints.ts @@ -162,9 +162,6 @@ const endpoints = { // Announcement CREATE_ANNOUNCEMENT: 'tutor_announcement_create', DELETE_ANNOUNCEMENT: 'tutor_announcement_delete', - - // Export Analytics - EXPORT_ANALYTICS: 'export_analytics', } as const; export default endpoints; From c680e2d0c1d1070ab6687f9528d9cdde51d6a8e4 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 9 Jan 2026 17:58:41 +0600 Subject: [PATCH 036/109] calender --- components/DateFilter.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/components/DateFilter.php b/components/DateFilter.php index e8f9f18985..6b9a8c5d44 100644 --- a/components/DateFilter.php +++ b/components/DateFilter.php @@ -71,6 +71,15 @@ class DateFilter extends BaseComponent { */ protected $placement = 'bottom-start'; + /** + * CSS class name used for the icon element. + * + * @since 4.0.0 + * + * @var string + */ + protected $icon_class; + /** * Set filter type. * @@ -107,6 +116,20 @@ public function placement( string $placement ): self { return $this; } + /** + * Set Icon Class. + * + * @since 4.0.0 + * + * @param string $icon_class CSS class name used for the icon element. + * + * @return self + */ + public function icon_class( string $icon_class ): self { + $this->icon_class = $icon_class; + return $this; + } + /** * Render the component. * @@ -128,9 +151,14 @@ public function get(): string { 'type' => 'multiple', 'selectionDatesMode' => 'multiple-ranged', ); + $button_classes .= ' tutor-gap-2'; $popover_classes .= ' tutor-range-calendar-popover'; + if ( ! empty( $this->icon_class ) ) { + $button_classes .= " {$this->icon_class}"; + } + // Default label for range if not set. if ( empty( $this->label ) ) { $this->label = $this->calculate_label(); From 441d20b1e2808d8da85388e9aa3eecb10e64308a Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 12 Jan 2026 13:29:56 +0600 Subject: [PATCH 037/109] calender filter --- .../src/js/frontend/dashboard/pages/instructor/home-charts.ts | 2 +- models/OrderModel.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/src/js/frontend/dashboard/pages/instructor/home-charts.ts b/assets/src/js/frontend/dashboard/pages/instructor/home-charts.ts index 83c919eab8..4c5d1abb95 100644 --- a/assets/src/js/frontend/dashboard/pages/instructor/home-charts.ts +++ b/assets/src/js/frontend/dashboard/pages/instructor/home-charts.ts @@ -544,7 +544,7 @@ export const overviewChart = (data: OverviewChartProps) => ({ }, createChartConfig(data: OverviewChartProps, colors: OverviewChartColors): ChartConfiguration<'line'> { - const dataLength = data.earnings.length; + const dataLength = data?.earnings?.length || data?.enrolled?.length; return { type: 'line', diff --git a/models/OrderModel.php b/models/OrderModel.php index cafa950175..357ca2658c 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -1138,7 +1138,8 @@ public function get_discounts_by_user( int $user_id, string $period = '', $start 0 ) ) AS total, - o.created_at_gmt AS date_format + o.created_at_gmt AS date_format, + DATE_FORMAT( o.created_at_gmt, '%b' ) AS month_name FROM {$this->table_name} o JOIN From c6828e4fab37a36c557287111d58c058c20abf21 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 12 Jan 2026 13:40:41 +0600 Subject: [PATCH 038/109] star icon issue fix --- templates/dashboard/instructor/analytics/star-rating.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dashboard/instructor/analytics/star-rating.php b/templates/dashboard/instructor/analytics/star-rating.php index 92eb9b04d6..49c9800cfd 100644 --- a/templates/dashboard/instructor/analytics/star-rating.php +++ b/templates/dashboard/instructor/analytics/star-rating.php @@ -28,8 +28,8 @@ $is_half = ! $is_full && ( $rating >= ( $i - 0.5 ) ); $icon_name = $is_full - ? Icon::STAR - : ( $is_half ? Icon::STAR_LINE : Icon::STAR_LINE ); // Todo: Half star icon. + ? Icon::STAR_FILL + : ( $is_half ? Icon::STAR_HALF : Icon::STAR_LINE ); $icon_html = tutor_utils()->render_svg_icon( $icon_name, 16, 16, array(), true ); // phpcs:ignore ?> From 3b2a6d674604e365abc80b9ed2bf6a67bb61d1c5 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 12 Jan 2026 17:01:01 +0600 Subject: [PATCH 039/109] graph issue fix --- models/OrderModel.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models/OrderModel.php b/models/OrderModel.php index 357ca2658c..af45809d23 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -1093,7 +1093,7 @@ public function get_discounts_by_user( int $user_id, string $period = '', $start $date_range_clause = ''; $period_clause = ''; $course_clause = ''; - $group_clause = ' GROUP BY DATE(date_format) '; + $group_clause = ' GROUP BY MONTH(date_format) '; $discount_clause = 'o.coupon_amount as total'; if ( $start_date && $end_date ) { @@ -1181,7 +1181,8 @@ public function get_discounts_by_user( int $user_id, string $period = '', $start 0 ) ) AS total, - o.created_at_gmt AS date_format + o.created_at_gmt AS date_format, + DATE_FORMAT( o.created_at_gmt, '%b' ) AS month_name FROM {$this->table_name} AS o WHERE 1 = %d AND o.order_status = 'completed' From f5ae3ea906319c1e71e166ea6ebf86434ad3d3cb Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 13 Jan 2026 13:23:11 +0600 Subject: [PATCH 040/109] Instructor home section array --- classes/Utils.php | 65 ++- templates/dashboard.php | 9 +- templates/dashboard/dashboard.php | 11 +- templates/dashboard/instructor/home.php | 563 ++++++++++++++++++++++++ 4 files changed, 635 insertions(+), 13 deletions(-) create mode 100644 templates/dashboard/instructor/home.php diff --git a/classes/Utils.php b/classes/Utils.php index 5a2f9a0697..f4f7cc7e6a 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -19,7 +19,6 @@ use Tutor\Helpers\QueryHelper; use Tutor\Traits\JsonResponse; use Tutor\Helpers\DateTimeHelper; -use Tutor\Helpers\UrlHelper; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -2241,12 +2240,12 @@ public function get_enrolled_courses_ids_by_user( $user_id = 0, $with_bundle_enr $with_bundle_enrolled_courses_clause = ''; if ( ! $with_bundle_enrolled_courses ) { $with_bundle_enrolled_courses_clause = $wpdb->prepare( - "AND NOT EXISTS ( + 'AND NOT EXISTS ( SELECT 1 FROM wp_postmeta pm WHERE pm.post_id = e.ID AND pm.meta_key = %s - )", + )', '_tutor_bundle_id' ); } @@ -9546,7 +9545,7 @@ public function should_show_dicussion_menu(): bool { */ public function default_menus(): array { $items = array( - 'index' => array( + 'index' => array( 'title' => __( 'Home', 'tutor' ), 'icon' => Icon::HOME, ), @@ -10817,4 +10816,62 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U return null; } + + /** + * Get sortable sections for the instructor home dashboard. + * + * @since 4.0.0 + * + * @return array[] { + * List of instructor dashboard sections. + * + * @type array { + * @type string $id Unique section identifier. + * @type string $label Section display label (translated). + * @type bool $is_active Whether the section is enabled. + * @type int $order Display order of the section. + * } + * } + */ + public function get_instructor_home_sortable_section() { + return array( + array( + 'id' => 'current_stats', + 'label' => esc_html__( 'Current Stats', 'tutor' ), + 'is_active' => true, + 'order' => 0, + ), + array( + 'id' => 'overview_chart', + 'label' => esc_html__( 'Earning Over Time', 'tutor' ), + 'is_active' => true, + 'order' => 1, + ), + array( + 'id' => 'course_completion_and_leader', + 'label' => esc_html__( 'Course Completion and Leader', 'tutor' ), + 'is_active' => true, + 'order' => 2, + ), + array( + 'id' => 'top_performing_courses', + 'label' => esc_html__( 'Top Performing Courses', 'tutor' ), + 'is_active' => true, + 'order' => 3, + ), + // @todo Will be added in the next version. + // array( + // 'id' => 'upcoming_tasks_and_activity', + // 'label' => esc_html__( 'Upcoming Tasks and Recent Activity', 'tutor' ), + // 'is_active' => true, + // 'order' => 4, + // ), + array( + 'id' => 'recent_reviews', + 'label' => esc_html__( 'Recent Student Reviews', 'tutor' ), + 'is_active' => true, + 'order' => 6, + ), + ); + } } diff --git a/templates/dashboard.php b/templates/dashboard.php index 6d772d6480..2d74edfa11 100644 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -101,11 +101,12 @@ do_action( 'tutor_load_dashboard_template_after', $dashboard_page_name ); } else { - if ( User::is_student() ) { - tutor_load_template( 'dashboard.student-dashboard' ); - } else { + // @TODO Need to update the condition to view dashboard. + // if ( User::is_student() ) { + // tutor_load_template( 'dashboard.student-dashboard' ); + // } else { tutor_load_template( 'dashboard.dashboard' ); - } + //} } ?>
diff --git a/templates/dashboard/dashboard.php b/templates/dashboard/dashboard.php index 39b7ba4100..47e44d8bb4 100644 --- a/templates/dashboard/dashboard.php +++ b/templates/dashboard/dashboard.php @@ -19,7 +19,7 @@ $incomplete_count = count( array_filter( $profile_completion, - function( $data ) { + function ( $data ) { return ! $data['is_set']; } ) @@ -101,7 +101,7 @@ function( $data ) {
@@ -109,8 +109,7 @@ function( $data ) {
@@ -128,11 +127,13 @@ function( $data ) { ); echo $alert_message; //phpcs:ignore - } + } } ?> + +
+ * @link https://themeum.com + * @since 4.0.0 + */ +use TUTOR\Icon; +use Tutor\Components\DateFilter; +use Tutor\Components\InputField; +use Tutor\Components\Constants\InputType; + +$sortable_sections = tutor_utils()->get_instructor_home_sortable_section(); +$sortable_sections_defaults = array_reduce( + $sortable_sections, + function ( $carry, $section ) { + $carry[ $section['id'] ] = $section['is_active'] ?? false; + return $carry; + }, + array() +); + +$sortable_sections_ids = array_reduce( + $sortable_sections, + function ( $carry, $section ) { + $carry[] = $section['id']; + return $carry; + }, + array() +); + +$stat_cards = array( + array( + 'variation' => 'success', + 'title' => esc_html__( 'Total Earnings', 'tutor' ), + 'icon' => Icon::EARNING, + 'value' => '$740.00', + 'change' => '+2', + 'data' => array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ), + ), + array( + 'variation' => 'brand', + 'title' => esc_html__( 'Total Courses', 'tutor' ), + 'icon' => Icon::COURSES, + 'value' => '12', + 'change' => '+2', + 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), + ), + array( + 'variation' => 'exception5', + 'title' => esc_html__( 'Total Students', 'tutor' ), + 'icon' => Icon::PASSED, + 'value' => '3000', + 'change' => '+2', + 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), + ), + array( + 'variation' => 'exception4', + 'title' => esc_html__( 'Avg. Rating', 'tutor' ), + 'icon' => Icon::STAR_LINE, + 'value' => '4.2', + 'change' => '+2', + 'data' => array( 4.5, 4.2, 3, 3, 2.8, 2, 4.5, 4.2, 3, 2, 1, 0 ), + ), +); + +$overview_chart_data = array( + 'earnings' => array( 30, 35, 45.2, 42.8, 41.5, 46.3, 52.1, 48.2, 45.8, 44.2, 46.5, 49.1, 52.8, 51.3 ), + 'enrolled' => array( 40, 60, 50.1, 48.5, 43.2, 48.9, 52.3, 49.7, 47.2, 48.8, 47.5, 49.2, 51.8, 53.2 ), + 'labels' => array( '', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', '' ), +); + +$course_completion_data = array( + 'enrolled' => array( + 'label' => esc_html__( 'Enrolled', 'tutor' ), + 'value' => 22000, + ), + 'completed' => array( + 'label' => esc_html__( 'Completed', 'tutor' ), + 'value' => 12000, + ), + 'in_progress' => array( + 'label' => esc_html__( 'In Progress', 'tutor' ), + 'value' => 5000, + ), + 'inactive' => array( + 'label' => esc_html__( 'Inactive', 'tutor' ), + 'value' => 4000, + ), + 'cancelled' => array( + 'label' => esc_html__( 'Cancelled', 'tutor' ), + 'value' => 2000, + ), +); + +$leaderboard_data = array( + array( + 'name' => esc_html__( 'John Doe', 'tutor' ), + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + 'no_of_courses' => 10, + 'completion_percentage' => 50, + ), + array( + 'name' => esc_html__( 'Jane Doe', 'tutor' ), + 'avatar' => 'https://i.pravatar.cc/300?u=a042581f4e29026704d', + 'no_of_courses' => 20, + 'completion_percentage' => 30, + ), + array( + 'name' => esc_html__( 'Bob Doe', 'tutor' ), + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826732d', + 'no_of_courses' => 30, + 'completion_percentage' => 70, + ), + array( + 'name' => esc_html__( 'Alice Doe', 'tutor' ), + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826752d', + 'no_of_courses' => 40, + 'completion_percentage' => 10, + ), + array( + 'name' => esc_html__( 'Charlie Doe', 'tutor' ), + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d823712d', + 'no_of_courses' => 50, + 'completion_percentage' => 40, + ), +); + +$top_performing_courses = array( + array( + 'name' => 'Complete Web Development Bootcamp', + 'url' => '#', + 'revenue' => 5000, + 'students' => 50, + ), + array( + 'name' => 'Complete Web Development Bootcamp', + 'url' => '#', + 'revenue' => 5000, + 'students' => 50, + ), + array( + 'name' => 'Complete Web Development Bootcamp', + 'url' => '#', + 'revenue' => 4000, + 'students' => 40, + ), + array( + 'name' => 'Complete Web Development Bootcamp', + 'url' => '#', + 'revenue' => 3000, + 'students' => 30, + ), +); + +$upcoming_tasks = array( + array( + 'name' => 'Complete Web Development Bootcamp', + 'date' => '2022-01-01 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor_assignments', + 'meta_info' => 'Web Dev 101', + ), + array( + 'name' => 'Live Q&A: React Hooks', + 'date' => '2022-01-02 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor-google-meet', + 'meta_info' => '67 registered', + ), + array( + 'name' => 'Quiz Closes: Python Functions', + 'date' => '2022-01-03 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor_quiz', + 'meta_info' => 'Python Basics', + ), + array( + 'name' => 'Live Q&A: Python Functions', + 'date' => '2022-01-04 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor_zoom_meeting', + 'meta_info' => '67 registered', + ), + array( + 'name' => 'Lesson Closes: Python Functions', + 'date' => '2022-01-05 10:00 AM', + 'url' => '#', + 'post_type' => 'lesson', + 'meta_info' => 'Python Basics', + ), +); + +$recent_activity = array( + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), + array( + 'course_name' => 'Complete Web Development Bootcamp', + 'course_url' => '#', + 'date' => '2022-01-01 10:00 AM', + 'meta' => 'enrolled in', + 'user' => array( + 'name' => 'John Doe', + 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', + ), + ), +); + +$recent_reviews = array( + array( + 'user' => array( + 'name' => 'Sarah Johnson', + 'avatar' => 'https://i.pravatar.cc/300?u=sarah', + ), + 'course_name' => 'Complete Web Development Bootcamp', + 'date' => '2022-01-01 08:00 AM', + 'rating' => 5, + 'review_text' => 'Outstanding course! The instructor explains complex concepts in a very clear and practical way. I landed my first dev job within 3 months of completing this course.', + 'helpful_count' => 12, + ), + array( + 'user' => array( + 'name' => 'Sarah Johnson', + 'avatar' => 'https://i.pravatar.cc/300?u=sarah', + ), + 'course_name' => 'Complete Web Development Bootcamp', + 'date' => '2022-01-01 08:00 AM', + 'rating' => 5, + 'review_text' => 'Outstanding course! The instructor explains complex concepts in a very clear and practical way. I landed my first dev job within 3 months of completing this course.', + 'helpful_count' => 12, + ), + array( + 'user' => array( + 'name' => 'Sarah Johnson', + 'avatar' => 'https://i.pravatar.cc/300?u=sarah', + ), + 'course_name' => 'Complete Web Development Bootcamp', + 'date' => '2022-01-01 08:00 AM', + 'rating' => 5, + 'review_text' => 'Outstanding course! The instructor explains complex concepts in a very clear and practical way. I landed my first dev job within 3 months of completing this course.', + 'helpful_count' => 12, + ), +); + +?> + +
+ +
+ + type( DateFilter::TYPE_RANGE )->placement( 'bottom-end' )->render(); ?> + +
+ + +
+
+ +
+ + type( InputType::CHECKBOX ) + ->name( "$section[id]" ) + ->label( $section['label'] ) + ->attr( 'x-bind', "register('$section[id]')" ) + ->render(); + ?> +
+ +
+
+
+
+ + +
+ +
+ isset( $card['variation'] ) ? $card['variation'] : 'enrolled', + 'card_title' => isset( $card['title'] ) ? $card['title'] : '', + 'icon' => isset( $card['icon'] ) ? $card['icon'] : '', + 'value' => isset( $card['value'] ) ? $card['value'] : '', + 'change' => isset( $card['change'] ) ? $card['change'] : '', + 'data' => isset( $card['data'] ) ? $card['data'] : array( 0, 0, 0 ), + 'show_graph' => true, + ) + ); + ?> +
+ +
+ + + $overview_chart_data, + ) + ); + ?> + +
+ + $course_completion_data, + ) + ); + ?> + + +
+
+ +
+ +
+ $item ) : ?> + $item_key, + 'item' => $item, + ) + ); + ?> + +
+
+
+ + +
+
+ +
+ +
+ $item ) : ?> + $item_key, + 'item' => $item, + ) + ); + ?> + +
+
+ +
+ +
+
+ +
+ +
+ + $item, + ) + ); + ?> + +
+
+ + +
+
+ +
+ +
+ + $item, + ) + ); + ?> + +
+
+
+ + +
+
+ +
+ +
+ + $review, + ) + ); + ?> + +
+
+
From 6ef89914c26d097fcb1a88a731fc55e499399335 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 13 Jan 2026 15:27:30 +0600 Subject: [PATCH 041/109] graph --- templates/dashboard/instructor/home.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index acf5c35508..796465bc5b 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -8,6 +8,8 @@ * @since 4.0.0 */ use TUTOR\Icon; +use TUTOR\Input; +use TUTOR_REPORT\Analytics; use Tutor\Components\DateFilter; use Tutor\Components\InputField; use Tutor\Components\Constants\InputType; @@ -31,6 +33,13 @@ function ( $carry, $section ) { array() ); +$user = wp_get_current_user(); +$time_period = Input::get( 'period', '' ); +$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; +$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; + +$earnings = Analytics::get_earnings_by_user( $user->ID, $time_period, $start_date, $end_date ); + $stat_cards = array( array( 'variation' => 'success', From 704990bcbf6d1fedc8d52e787c3279bc2bf09f70 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 13 Jan 2026 15:49:56 +0600 Subject: [PATCH 042/109] star icon issue fix --- templates/demo-components/dashboard/components/star-rating.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/demo-components/dashboard/components/star-rating.php b/templates/demo-components/dashboard/components/star-rating.php index 6b7d990048..c7b0a9ba75 100644 --- a/templates/demo-components/dashboard/components/star-rating.php +++ b/templates/demo-components/dashboard/components/star-rating.php @@ -28,7 +28,7 @@ $is_half = ! $is_full && ( $rating >= ( $i - 0.5 ) ); $icon_name = $is_full - ? Icon::STAR + ? Icon::STAR_FILL : ( $is_half ? Icon::STAR_LINE : Icon::STAR_LINE ); // Todo: Half star icon. $icon_html = tutor_utils()->render_svg_icon( $icon_name, 16, 16, array(), true ); // phpcs:ignore From 71de455a1cefa8bce75d915e5935f651e4a53bf2 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 13 Jan 2026 18:07:13 +0600 Subject: [PATCH 043/109] total earning value added --- templates/dashboard/instructor/home.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 796465bc5b..65e9b6863e 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -45,7 +45,7 @@ function ( $carry, $section ) { 'variation' => 'success', 'title' => esc_html__( 'Total Earnings', 'tutor' ), 'icon' => Icon::EARNING, - 'value' => '$740.00', + 'value' => wp_kses_post( tutor_utils()->tutor_price( $earnings['total_earnings'] ?? 0 ) ), 'change' => '+2', 'data' => array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ), ), From 4ce5e6ae45eaf67203fe43a33835fef80199cd1d Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 14 Jan 2026 17:58:07 +0600 Subject: [PATCH 044/109] state card --- classes/Instructor.php | 133 ++++++++++++++++++++++++ templates/dashboard/instructor/home.php | 32 ++++-- 2 files changed, 154 insertions(+), 11 deletions(-) diff --git a/classes/Instructor.php b/classes/Instructor.php index 7acf3f1dc2..e776851ee7 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -10,6 +10,9 @@ namespace TUTOR; +use DateTime; +use Tutor\Helpers\QueryHelper; + if ( ! defined( 'ABSPATH' ) ) { exit; } @@ -416,4 +419,134 @@ public function update_instructor_meta( int $user_id ) { do_action( 'tutor_new_instructor_after', $user_id ); } + + public static function get_total_earnings_by_instructor( $user_id, $start_date, $end_date ) { + + global $wpdb; + $user_id = sanitize_text_field( $user_id ); + $start_date = sanitize_text_field( $start_date ); + $end_date = sanitize_text_field( $end_date ); + + $period_query = ''; + $group_query = ' GROUP BY MONTH(date_format) '; + + if ( ! empty( $start_date ) && ! empty( $end_date ) ) { + $period_query = " AND DATE(earnings.created_at) BETWEEN CAST('$start_date' AS DATE) AND CAST('$end_date' AS DATE) "; + $group_query = ' GROUP BY MONTH(date_format) '; + } + + if ( empty( $start_date ) && empty( $end_date ) ) { + $group_query = ' GROUP BY YEAR(date_format) '; + } + + // Get statuses. + $complete_status = QueryHelper::prepare_in_clause( tutor_utils()->get_earnings_completed_statuses() ); + + $amount_type = current_user_can( 'administrator' ) ? 'earnings.admin_amount' : 'earnings.instructor_amount'; + $amount_rate = current_user_can( 'administrator' ) ? 'earnings.admin_rate' : 'earnings.instructor_rate'; + + $amount_condition = "CASE + WHEN orders.tax_type = 'inclusive' AND earnings.course_price_grand_total > 0 + THEN ( earnings.course_price_grand_total - orders.tax_amount ) * ( $amount_rate/100 ) + ELSE $amount_type + END"; + + $select_columns = array( "SUM($amount_condition) AS total" ); + $primary_table = "{$wpdb->prefix}tutor_earnings"; + $joining_table = array( + array( + 'type' => 'LEFT', + 'table' => "{$wpdb->prefix}tutor_orders", + 'on' => 'earnings.order_id = orders.id', + ), + ); + + $result = QueryHelper::query( + $primary_table, + array( + 'select' => $select_columns, + 'alias' => 'earnings', + 'where' => array( + 'earnings.user_id' => $user_id, + 'earnings.order_status' => array( 'IN', $complete_status ), + ), + 'joins' => $joining_table, + ) + ); + + return $result[0]->total ?? 0; + } + + public static function get_stat_card_subtitle( $start_date, $end_date, $current_data, $previous_data ) { + + $current_time = time(); + $today = gmdate( 'Y-m-d', $current_time ); + $start = new DateTime( 'now' ); + $end = new DateTime( 'now' ); + $days = $start->diff( $end )->days; + $first_day_last_month = strtotime( 'first day of previous month', $current_time ); + + if ( $start_date === $end_date === $today ) { + $time_span = 'today'; + } + + switch ( $days ) { + + case 1: + $time_span = 'yesterday'; + break; + + case 6: + $time_span = 'last 7 days'; + break; + + case 13: + $time_span = 'last 14 days'; + break; + + case 29: + $time_span = 'last 30 days'; + break; + + case gmdate( 'Y-m-01', $current_time ) === $start_date && gmdate( 'Y-m-t', $current_time ) === $end_date: + $time_span = 'this-month'; + break; + + case gmdate( 'Y-m-d', $first_day_last_month ) === $start_date && gmdate( 'Y-m-t', $first_day_last_month ) === $end_date: + $time_span = 'last-month'; + break; + + case gmdate( 'Y-01-01', strtotime( '-1 year', $current_time ) ) === gmdate( 'Y-12-31', strtotime( '-1 year', $current_time ) ) === $end_date: + $time_span = 'last-year'; + break; + + default: + $start_format = date_i18n( 'M j', strtotime( $start_date ) ); + $end_format = date_i18n( 'M j', strtotime( $end_date ) ); + + $time_span = sprintf( __( ' from %1$s-%2$s', 'tutor' ), $start_format, $end_format ); + break; + + } + + $data = $current_data - $previous_data; + $symbol = $data < 0 ? '-' : '+'; + + return $symbol . wp_kses_post( tutor_utils()->tutor_price( abs( $data ) ?? 0 ) ) . $time_span; + } + + public static function get_comparison_date_range( $selected_start_date, $selected_end_date ) { + + $start = new DateTime( $selected_start_date ); + $end = new DateTime( $selected_end_date ); + $days = $start->diff( $end )->days; + + $previous_start_date = gmdate( 'Y-m-d', strtotime( $start->format( 'Y-m-d H:i:s' ) . "- $days days" ) ); + $previous_end_date = gmdate( 'Y-m-d', strtotime( $end->format( 'Y-m-d H:i:s' ) . "- $days days" ) ); + + return array( + 'previous_start_date' => $previous_start_date, + 'previous_end_date' => $previous_end_date, + ); + } } diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 65e9b6863e..a0dd720a39 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -9,6 +9,7 @@ */ use TUTOR\Icon; use TUTOR\Input; +use TUTOR\Instructor; use TUTOR_REPORT\Analytics; use Tutor\Components\DateFilter; use Tutor\Components\InputField; @@ -33,21 +34,30 @@ function ( $carry, $section ) { array() ); -$user = wp_get_current_user(); -$time_period = Input::get( 'period', '' ); -$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; -$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; +$user = wp_get_current_user(); +$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; +$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; +$time_period = ''; +$stat_card_content = ''; + +if ( empty( $start_date ) && empty( $end_date ) ) { + $time_period = 'monthly'; + $stat_card_content = __( ' this month', 'tutor' ); +} + +$total_earnings = Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date )['total_earnings'] ?? 0; +$previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); +$previous_total_earnings = Analytics::get_earnings_by_user( $user->ID, '', $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )['total_earnings'] ?? 0; -$earnings = Analytics::get_earnings_by_user( $user->ID, $time_period, $start_date, $end_date ); $stat_cards = array( array( 'variation' => 'success', 'title' => esc_html__( 'Total Earnings', 'tutor' ), 'icon' => Icon::EARNING, - 'value' => wp_kses_post( tutor_utils()->tutor_price( $earnings['total_earnings'] ?? 0 ) ), - 'change' => '+2', - 'data' => array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ), + 'value' => wp_kses_post( tutor_utils()->tutor_price( $total_earnings ) ), + 'change' => Instructor::get_stat_card_subtitle( $start_date, $end_date, $total_earnings, $previous_total_earnings ), + // 'data' => array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ), @todo will be added later. ), array( 'variation' => 'brand', @@ -55,7 +65,7 @@ function ( $carry, $section ) { 'icon' => Icon::COURSES, 'value' => '12', 'change' => '+2', - 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), + // 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), ), array( 'variation' => 'exception5', @@ -63,7 +73,7 @@ function ( $carry, $section ) { 'icon' => Icon::PASSED, 'value' => '3000', 'change' => '+2', - 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), + // 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), ), array( 'variation' => 'exception4', @@ -71,7 +81,7 @@ function ( $carry, $section ) { 'icon' => Icon::STAR_LINE, 'value' => '4.2', 'change' => '+2', - 'data' => array( 4.5, 4.2, 3, 3, 2.8, 2, 4.5, 4.2, 3, 2, 1, 0 ), + // 'data' => array( 4.5, 4.2, 3, 3, 2.8, 2, 4.5, 4.2, 3, 2, 1, 0 ), ), ); From 5af65c12a9c0b67d2356fcacaa619f9680bc36e1 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 15 Jan 2026 18:07:59 +0600 Subject: [PATCH 045/109] total course count --- classes/Instructor.php | 158 ++++++++++-------------- models/CourseModel.php | 67 +++++++++- templates/dashboard/instructor/home.php | 34 +++-- 3 files changed, 141 insertions(+), 118 deletions(-) diff --git a/classes/Instructor.php b/classes/Instructor.php index e776851ee7..7b8cef6f45 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -11,7 +11,7 @@ namespace TUTOR; use DateTime; -use Tutor\Helpers\QueryHelper; +use DateInterval; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -420,129 +420,97 @@ public function update_instructor_meta( int $user_id ) { do_action( 'tutor_new_instructor_after', $user_id ); } - public static function get_total_earnings_by_instructor( $user_id, $start_date, $end_date ) { + public static function get_stat_card_comparison_subtitle( + string $start_date, + string $end_date, + float $current_data, + float $previous_data, + bool $price = true + ): string { - global $wpdb; - $user_id = sanitize_text_field( $user_id ); - $start_date = sanitize_text_field( $start_date ); - $end_date = sanitize_text_field( $end_date ); + $diff = $current_data - $previous_data; + $symbol = $diff < 0 ? '-' : ( $diff > 0 ? '+' : '' ); + $diff = $price ? wp_kses_post( tutor_utils()->tutor_price( abs( $diff ) ) ) : abs( $diff ); - $period_query = ''; - $group_query = ' GROUP BY MONTH(date_format) '; + $start = new DateTime( $start_date ); + $end = new DateTime( $end_date ); + $days = (int) $start->diff( $end )->days; - if ( ! empty( $start_date ) && ! empty( $end_date ) ) { - $period_query = " AND DATE(earnings.created_at) BETWEEN CAST('$start_date' AS DATE) AND CAST('$end_date' AS DATE) "; - $group_query = ' GROUP BY MONTH(date_format) '; - } + $time_span = empty( $start_date ) && empty( $end_date ) + ? __( 'this month', 'tutor' ) + : self::get_comparison_period_label( $start_date, $end_date, $days ); - if ( empty( $start_date ) && empty( $end_date ) ) { - $group_query = ' GROUP BY YEAR(date_format) '; - } + return "{$symbol}{$diff} {$time_span}"; + } - // Get statuses. - $complete_status = QueryHelper::prepare_in_clause( tutor_utils()->get_earnings_completed_statuses() ); + private static function get_comparison_period_label( string $start_date, string $end_date, int $days ): string { - $amount_type = current_user_can( 'administrator' ) ? 'earnings.admin_amount' : 'earnings.instructor_amount'; - $amount_rate = current_user_can( 'administrator' ) ? 'earnings.admin_rate' : 'earnings.instructor_rate'; + $time_zone = wp_timezone(); + $now = new DateTime( 'now', $time_zone ); + $today = wp_date( 'Y-m-d', null, $time_zone ); - $amount_condition = "CASE - WHEN orders.tax_type = 'inclusive' AND earnings.course_price_grand_total > 0 - THEN ( earnings.course_price_grand_total - orders.tax_amount ) * ( $amount_rate/100 ) - ELSE $amount_type - END"; + $this_month_start = $now->modify( 'first day of this month' )->format( 'Y-m-d' ); + $this_month_end = $now->modify( 'last day of this month' )->format( 'Y-m-d' ); - $select_columns = array( "SUM($amount_condition) AS total" ); - $primary_table = "{$wpdb->prefix}tutor_earnings"; - $joining_table = array( - array( - 'type' => 'LEFT', - 'table' => "{$wpdb->prefix}tutor_orders", - 'on' => 'earnings.order_id = orders.id', - ), - ); + $last_month_start = $now->modify( 'first day of last month' )->format( 'Y-m-d' ); + $last_month_end = $now->modify( 'last day of last month' )->format( 'Y-m-d' ); - $result = QueryHelper::query( - $primary_table, - array( - 'select' => $select_columns, - 'alias' => 'earnings', - 'where' => array( - 'earnings.user_id' => $user_id, - 'earnings.order_status' => array( 'IN', $complete_status ), - ), - 'joins' => $joining_table, - ) - ); + $last_year_start = $now->modify( 'first day of January last year' )->format( 'Y-m-d' ); + $last_year_end = $now->modify( 'last day of December last year' )->format( 'Y-m-d' ); - return $result[0]->total ?? 0; - } - - public static function get_stat_card_subtitle( $start_date, $end_date, $current_data, $previous_data ) { - - $current_time = time(); - $today = gmdate( 'Y-m-d', $current_time ); - $start = new DateTime( 'now' ); - $end = new DateTime( 'now' ); - $days = $start->diff( $end )->days; - $first_day_last_month = strtotime( 'first day of previous month', $current_time ); - - if ( $start_date === $end_date === $today ) { - $time_span = 'today'; + if ( $start_date === $today && $end_date === $today ) { + return __( 'today', 'tutor' ); } - switch ( $days ) { + switch ( true ) { - case 1: - $time_span = 'yesterday'; - break; + case ( 0 === $days && $today !== $start_date ): + return __( 'from yesterday', 'tutor' ); - case 6: - $time_span = 'last 7 days'; - break; + case ( 6 === $days ): + return __( 'from last 7 days', 'tutor' ); - case 13: - $time_span = 'last 14 days'; - break; + case ( 13 === $days ): + return __( 'from last 14 days', 'tutor' ); - case 29: - $time_span = 'last 30 days'; - break; + case ( 29 === $days ): + return __( 'from last 30 days', 'tutor' ); - case gmdate( 'Y-m-01', $current_time ) === $start_date && gmdate( 'Y-m-t', $current_time ) === $end_date: - $time_span = 'this-month'; - break; + case ( $start_date === $this_month_start && $end_date === $this_month_end ): + return __( 'from this month', 'tutor' ); - case gmdate( 'Y-m-d', $first_day_last_month ) === $start_date && gmdate( 'Y-m-t', $first_day_last_month ) === $end_date: - $time_span = 'last-month'; - break; + case ( $start_date === $last_month_start && $end_date === $last_month_end ): + return __( 'from last month', 'tutor' ); - case gmdate( 'Y-01-01', strtotime( '-1 year', $current_time ) ) === gmdate( 'Y-12-31', strtotime( '-1 year', $current_time ) ) === $end_date: - $time_span = 'last-year'; - break; + case ( $start_date === $last_year_start && $end_date === $last_year_end ): + return __( 'from last year', 'tutor' ); default: - $start_format = date_i18n( 'M j', strtotime( $start_date ) ); - $end_format = date_i18n( 'M j', strtotime( $end_date ) ); - - $time_span = sprintf( __( ' from %1$s-%2$s', 'tutor' ), $start_format, $end_format ); - break; - + return sprintf( + /* translators: 1: formatted start date, 2: formatted end date */ + __( 'from %1$s–%2$s', 'tutor' ), + wp_date( 'M j', strtotime( $start_date ), $time_zone ), + wp_date( 'M j', strtotime( $end_date ), $time_zone ) + ); } - - $data = $current_data - $previous_data; - $symbol = $data < 0 ? '-' : '+'; - - return $symbol . wp_kses_post( tutor_utils()->tutor_price( abs( $data ) ?? 0 ) ) . $time_span; } public static function get_comparison_date_range( $selected_start_date, $selected_end_date ) { + if ( empty( $selected_start_date ) && empty( $selected_end_date ) ) { + $now = new DateTime(); + return array( + 'previous_start_date' => $now->modify( 'first day of this month' )->format( 'Y-m-d' ), + 'previous_end_date' => $now->modify( 'last day of this month' )->format( 'Y-m-d' ), + ); + } + $start = new DateTime( $selected_start_date ); $end = new DateTime( $selected_end_date ); - $days = $start->diff( $end )->days; + $days = $start->diff( $end )->days + 1; - $previous_start_date = gmdate( 'Y-m-d', strtotime( $start->format( 'Y-m-d H:i:s' ) . "- $days days" ) ); - $previous_end_date = gmdate( 'Y-m-d', strtotime( $end->format( 'Y-m-d H:i:s' ) . "- $days days" ) ); + $previous_start_date = $start->sub( DateInterval::createFromDateString( "$days days" ) )->format( 'Y-m-d' ); + $previous_end_date = $end->sub( DateInterval::createFromDateString( "$days days" ) )->format( 'Y-m-d' ); return array( 'previous_start_date' => $previous_start_date, diff --git a/models/CourseModel.php b/models/CourseModel.php index 062b10920b..9ad3648ee4 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -327,12 +327,12 @@ public static function get_courses_by_instructor( //phpcs:disable $query = $wpdb->prepare( "SELECT $select_col - FROM $wpdb->posts + FROM $wpdb->posts LEFT JOIN {$wpdb->usermeta} - ON $wpdb->usermeta.user_id = %d - AND $wpdb->usermeta.meta_key = %s - AND $wpdb->usermeta.meta_value = $wpdb->posts.ID - WHERE 1 = 1 {$where_post_status} + ON $wpdb->usermeta.user_id = %d + AND $wpdb->usermeta.meta_key = %s + AND $wpdb->usermeta.meta_value = $wpdb->posts.ID + WHERE 1 = 1 {$where_post_status} AND $wpdb->posts.post_type IN ({$post_types}) AND ($wpdb->posts.post_author = %d OR $wpdb->usermeta.user_id = %d) {$search_sql} @@ -1344,4 +1344,61 @@ public static function get_enrolled_courses_by_user( $user_id = 0, $post_status return false; } + + public static function get_courses_by_between_dates( $start_date, $end_date, $user_id ) { + + $by_date = self::get_courses_by_args( + array( + 'post_author' => $user_id, + 'posts_per_page' => -1, + 'fields' => 'ids', + 'date_query' => array( + 'column' => 'post_date_gmt', + 'before' => $end_date, + 'after' => $start_date, + ), + ) + ); + + $ids = array(); + + foreach ( $by_date->posts as $post ) { + $date = get_post_meta( $post, '_wp_old_date', true ); + + if ( empty( $date ) ) { + $ids[] = $post; + continue; + } + + $date = strtotime( $date ); + + if ( ! empty( $date ) && strtotime( $start_date ) <= $date && strtotime( $end_date ) >= $date ) { + $ids[] = $post; + } + } + + $by_meta = self::get_courses_by_args( + array( + 'post_author' => $user_id, + 'posts_per_page' => -1, + 'fields' => 'ids', + 'meta_key' => '_wp_old_date', + 'meta_value' => array( $start_date, $end_date ), + 'meta_compare' => 'BETWEEN', + 'meta_type' => 'DATE', + ) + ); + + foreach ( $by_meta->posts as $post ) { + $date = get_post_meta( $post, '_wp_old_date', true ); + + $date = strtotime( $date ); + + if ( ! empty( $date ) && strtotime( $start_date ) <= $date && strtotime( $end_date ) >= $date ) { + $ids[] = $post; + } + } + + return count( array_unique( $ids ) ); + } } diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index a0dd720a39..905a596609 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -11,6 +11,7 @@ use TUTOR\Input; use TUTOR\Instructor; use TUTOR_REPORT\Analytics; +use Tutor\Models\CourseModel; use Tutor\Components\DateFilter; use Tutor\Components\InputField; use Tutor\Components\Constants\InputType; @@ -34,21 +35,18 @@ function ( $carry, $section ) { array() ); -$user = wp_get_current_user(); -$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; -$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; -$time_period = ''; -$stat_card_content = ''; +$user = wp_get_current_user(); +$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; +$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; +$previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); -if ( empty( $start_date ) && empty( $end_date ) ) { - $time_period = 'monthly'; - $stat_card_content = __( ' this month', 'tutor' ); -} - -$total_earnings = Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date )['total_earnings'] ?? 0; -$previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); -$previous_total_earnings = Analytics::get_earnings_by_user( $user->ID, '', $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )['total_earnings'] ?? 0; +// Total Earnings. +$total_earnings = Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date )['total_earnings'] ?? 0; +$previous_period_earnings = Analytics::get_earnings_by_user( $user->ID, '', $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )['total_earnings'] ?? 0; +// Total Courses. +$total_courses = CourseModel::get_courses_by_args( array( 'post_author' => $user->ID ) ); +$previous_period_courses = CourseModel::get_courses_by_between_dates( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); $stat_cards = array( array( @@ -56,16 +54,16 @@ function ( $carry, $section ) { 'title' => esc_html__( 'Total Earnings', 'tutor' ), 'icon' => Icon::EARNING, 'value' => wp_kses_post( tutor_utils()->tutor_price( $total_earnings ) ), - 'change' => Instructor::get_stat_card_subtitle( $start_date, $end_date, $total_earnings, $previous_total_earnings ), + 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_earnings, $previous_period_earnings ), // 'data' => array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ), @todo will be added later. ), array( 'variation' => 'brand', 'title' => esc_html__( 'Total Courses', 'tutor' ), 'icon' => Icon::COURSES, - 'value' => '12', - 'change' => '+2', - // 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), + 'value' => $total_courses->post_count, + 'change' => $previous_period_courses, + // 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), @todo will be added later. ), array( 'variation' => 'exception5', @@ -430,7 +428,7 @@ class="tutor-flex tutor-gap-5" 'value' => isset( $card['value'] ) ? $card['value'] : '', 'change' => isset( $card['change'] ) ? $card['change'] : '', 'data' => isset( $card['data'] ) ? $card['data'] : array( 0, 0, 0 ), - 'show_graph' => true, + 'show_graph' => false, ) ); ?> From 73253a91abd602a4ee374306986f7ff354888b76 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Fri, 16 Jan 2026 05:22:28 +0600 Subject: [PATCH 046/109] course total --- classes/Instructor.php | 3 +- models/CourseModel.php | 51 ++++++++++--------------- templates/dashboard/instructor/home.php | 2 +- 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/classes/Instructor.php b/classes/Instructor.php index 7b8cef6f45..127ebd873a 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -428,7 +428,8 @@ public static function get_stat_card_comparison_subtitle( bool $price = true ): string { - $diff = $current_data - $previous_data; + $diff = (empty($start_date) && empty($end_date) && 0 === intval($previous_data)) + ? 0 : $current_data - $previous_data; $symbol = $diff < 0 ? '-' : ( $diff > 0 ? '+' : '' ); $diff = $price ? wp_kses_post( tutor_utils()->tutor_price( abs( $diff ) ) ) : abs( $diff ); diff --git a/models/CourseModel.php b/models/CourseModel.php index 9ad3648ee4..86cb89e7ff 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -1347,41 +1347,27 @@ public static function get_enrolled_courses_by_user( $user_id = 0, $post_status public static function get_courses_by_between_dates( $start_date, $end_date, $user_id ) { + $common_args = [ + 'post_author' => $user_id, + 'posts_per_page' => -1, + 'fields' => 'ids', + ]; + $by_date = self::get_courses_by_args( + $common_args + array( - 'post_author' => $user_id, - 'posts_per_page' => -1, - 'fields' => 'ids', 'date_query' => array( 'column' => 'post_date_gmt', 'before' => $end_date, 'after' => $start_date, + 'inclusive' => true ), ) ); - $ids = array(); - - foreach ( $by_date->posts as $post ) { - $date = get_post_meta( $post, '_wp_old_date', true ); - - if ( empty( $date ) ) { - $ids[] = $post; - continue; - } - - $date = strtotime( $date ); - - if ( ! empty( $date ) && strtotime( $start_date ) <= $date && strtotime( $end_date ) >= $date ) { - $ids[] = $post; - } - } - $by_meta = self::get_courses_by_args( + $common_args + array( - 'post_author' => $user_id, - 'posts_per_page' => -1, - 'fields' => 'ids', 'meta_key' => '_wp_old_date', 'meta_value' => array( $start_date, $end_date ), 'meta_compare' => 'BETWEEN', @@ -1389,16 +1375,21 @@ public static function get_courses_by_between_dates( $start_date, $end_date, $us ) ); - foreach ( $by_meta->posts as $post ) { - $date = get_post_meta( $post, '_wp_old_date', true ); + $post_ids = array_unique(array_merge((array)$by_date->posts, (array)$by_meta->posts)); - $date = strtotime( $date ); + $filtered = array_filter( + $post_ids, + function ( int $post_id ) use ( $start_date, $end_date ): bool { + $old_date = get_post_meta( $post_id, '_wp_old_date', true ); // first value - if ( ! empty( $date ) && strtotime( $start_date ) <= $date && strtotime( $end_date ) >= $date ) { - $ids[] = $post; + if ( empty( $old_date ) ) { + return true; + } + + return strtotime( $start_date ) <= $old_date && strtotime( $end_date ) >= $old_date ; } - } + ); - return count( array_unique( $ids ) ); + return count( $filtered); } } diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 905a596609..6b0f0081c4 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -362,7 +362,7 @@ class="tutor-flex tutor-flex-column tutor-gap-6 tutor-mt-7"
- type( DateFilter::TYPE_RANGE )->placement( 'bottom-end' )->render(); ?> + type( DateFilter::TYPE_RANGE )->placement( 'bottom-start' )->render(); ?>
$item ) : ?> path . 'templates/dashboard/instructor/home/top-performing-course-item.php'; - echo tutor_utils()->render_template( - $template_path, + tutor_load_template_from_custom_path( + $template_path . 'top-performing-course-item.php', array( 'item_key' => $item_key, 'item' => $item, ), - 'include' + false ) ?> diff --git a/templates/dashboard/instructor/home/top-performing-course-filter.php b/templates/dashboard/instructor/home/top-performing-course-filter.php new file mode 100644 index 0000000000..65dd523c85 --- /dev/null +++ b/templates/dashboard/instructor/home/top-performing-course-filter.php @@ -0,0 +1,47 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +use TUTOR\Icon; + +$options = isset( $data['options'] ) ? $data['options'] : array(); +$selected = isset( $data['selected'] ) ? $data['selected'] : ''; + +?> + +
+ +
+
+ $option ) : ?> + + + + +
+
+
From 397328c4ecd408c3dbe8862473cd170390fd4139 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Wed, 21 Jan 2026 04:38:46 +0600 Subject: [PATCH 058/109] Top Performing error fix --- classes/Instructor.php | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/classes/Instructor.php b/classes/Instructor.php index 2eacedbd2b..76fe7f121c 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -728,7 +728,7 @@ public static function get_top_performing_courses_by_instructor( $instructor_id, $end_date = $args['end_date'] ?? null; $order_by = 'revenue' === $args['order_by'] ? 'total_revenue' : 'total_student'; - $complete_status = QueryHelper::prepare_in_clause( tutor_utils()->get_earnings_completed_statuses() ); + $complete_status = tutor_utils()->get_earnings_completed_statuses(); $amount_type = current_user_can( 'administrator' ) ? 'earnings.admin_amount' : 'earnings.instructor_amount'; $amount_rate = current_user_can( 'administrator' ) ? 'earnings.admin_rate' : 'earnings.instructor_rate'; @@ -739,20 +739,21 @@ public static function get_top_performing_courses_by_instructor( $instructor_id, ELSE $amount_type END"; - $earning_where_clause = QueryHelper::prepare_where_clause( - array( - 'earnings.user_id' => $instructor_id, - 'earnings.order_status' => array('IN', $complete_status), - ) - ); - - $enrollment_where_clause = QueryHelper::prepare_where_clause(array('post_type' => 'tutor_enrolled')); + $earning_where_clause = array( + 'earnings.user_id' => $instructor_id, + 'earnings.order_status' => array('IN', $complete_status), + ); + + $enrollment_where_clause = array('post_type' => 'tutor_enrolled'); if (!empty($start_date) && !empty($end_date)) { - $earning_where_clause['earnings.created_at'] = array('BETWEEN', array($start_date, $end_date)); - $enrollment_where_clause['post_date'] = array('BETWEEN', array($start_date, $end_date)); + $earning_where_clause['earnings.created_at'] = array( 'BETWEEN', array( $start_date, $end_date ) ); + $enrollment_where_clause['post_date'] = array( 'BETWEEN', array( $start_date, $end_date ) ); } + $earning_where_clause = QueryHelper::prepare_where_clause($earning_where_clause); + $enrollment_where_clause = QueryHelper::prepare_where_clause($enrollment_where_clause); + $earnings_sql = "SELECT earnings.course_id, SUM($amount_condition) AS total_revenue From 69f32d77c38e636bf236b992f3ba543ddb5eaf88 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Wed, 21 Jan 2026 05:46:53 +0600 Subject: [PATCH 059/109] Instructor home (recent Reviews) --- classes/Instructor.php | 17 +--- templates/dashboard/instructor/home.php | 98 +++++++++---------- .../home/recent-student-review-item.php | 80 +++++++++++++++ 3 files changed, 129 insertions(+), 66 deletions(-) create mode 100644 templates/dashboard/instructor/home/recent-student-review-item.php diff --git a/classes/Instructor.php b/classes/Instructor.php index 76fe7f121c..9fddf6eb8f 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -617,8 +617,8 @@ public static function get_instructor_total_students_by_date_range( $start_date, * Get course completion distribution data for a specific instructor. * * @since 4.0.0 - * - * @param int $instructor_id Instructor user ID. + * + * @param array $instructor_course_ids Optional list of course IDs. * * @return array { * Enrollment distribution counts. @@ -630,7 +630,7 @@ public static function get_instructor_total_students_by_date_range( $start_date, * @type int $cancelled Number of cancelled enrollments. * } */ - public static function get_course_completion_distribution_data_by_instructor( $instructor_id ) { + public static function get_course_completion_distribution_data_by_instructor( $instructor_course_ids = array() ) { global $wpdb; @@ -643,18 +643,9 @@ public static function get_course_completion_distribution_data_by_instructor( $i ); $cancel_statuses = array( 'cancel', 'canceled', 'cancelled' ); - $post_statuses = array_merge( $cancel_statuses, array( 'completed' ) ); - $instructor_course_ids = CourseModel::get_courses_by_args( - array( - 'post_author' => $instructor_id, - 'posts_per_page' => -1, - 'fields' => 'ids', - ) - )->posts; - - if ( empty( $instructor_course_ids ) ) { + if (empty($instructor_course_ids)){ return $counts; } diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 3e0abc8ab7..6ca701c11c 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -15,6 +15,7 @@ use Tutor\Components\DateFilter; use Tutor\Components\InputField; use Tutor\Components\Constants\InputType; +use Tutor\Helpers\QueryHelper; $sortable_sections = tutor_utils()->get_instructor_home_sortable_section(); $sortable_sections_defaults = array_reduce( @@ -35,6 +36,14 @@ function ( $carry, $section ) { array() ); +$instructor_course_ids = CourseModel::get_courses_by_args( + array( + 'post_author' => $instructor_id, + 'posts_per_page' => -1, + 'fields' => 'ids', + ) +)->posts; + $template_path = tutor()->path . 'templates/dashboard/instructor/home/'; $user = wp_get_current_user(); $start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; @@ -105,7 +114,7 @@ function ( $carry, $section ) { ); // Course Completion Distribution. -$course_completion_distribution = Instructor::get_course_completion_distribution_data_by_instructor( $user->ID ); +$course_completion_distribution = Instructor::get_course_completion_distribution_data_by_instructor( $instructor_course_ids ); $course_completion_data = array( 'enrolled' => array( @@ -327,44 +336,25 @@ function ( $course ) { // ); // Recent Reviews. -$recent_reviews = Analytics::get_reviews(); -$recent_reviews = array( - array( - 'user' => array( - 'name' => 'Sarah Johnson', - 'avatar' => 'https://i.pravatar.cc/300?u=sarah', - ), - 'course_name' => 'Complete Web Development Bootcamp', - 'date' => '2022-01-01 08:00 AM', - 'rating' => 5, - 'review_text' => 'Outstanding course! The instructor explains complex concepts in a very clear and practical way. I landed my first dev job within 3 months of completing this course.', - 'helpful_count' => 12, - ), - array( - 'user' => array( - 'name' => 'Sarah Johnson', - 'avatar' => 'https://i.pravatar.cc/300?u=sarah', - ), - 'course_name' => 'Complete Web Development Bootcamp', - 'date' => '2022-01-01 08:00 AM', - 'rating' => 5, - 'review_text' => 'Outstanding course! The instructor explains complex concepts in a very clear and practical way. I landed my first dev job within 3 months of completing this course.', - 'helpful_count' => 12, - ), - array( +$review_where = array('comment_post_ID' => array( 'IN', $instructor_course_ids)); +if (!empty($start_date) && !empty($end_date)) { + $review_where['comment_date'] = array('BETWEEN', array( $start_date, $end_date )); +} +$review_args = array('where' => QueryHelper::prepare_where_clause($review_where)); +$reviews = Analytics::get_reviews(3,$review_args); +$recent_reviews = array_map(function($review){ + return array( 'user' => array( - 'name' => 'Sarah Johnson', - 'avatar' => 'https://i.pravatar.cc/300?u=sarah', + 'name' => $review->display_name, + 'avatar' => get_avatar_url($review->user_id), ), - 'course_name' => 'Complete Web Development Bootcamp', - 'date' => '2022-01-01 08:00 AM', - 'rating' => 5, - 'review_text' => 'Outstanding course! The instructor explains complex concepts in a very clear and practical way. I landed my first dev job within 3 months of completing this course.', - 'helpful_count' => 12, - ), -); - - ?> + 'course_name' => get_the_title($review->comment_post_ID), + 'date' => $review->comment_date, + 'rating' => $review->rating, + 'review_text' => $review->comment_content + ); +}, $reviews); +?> +
- + $item, - ) - ); + // tutor_load_template( + // 'demo-components.dashboard.components.instructor.home.recent-activity-item', + // array( + // 'item' => $item, + // ) + // ); ?> - +
-
+
-->
+
$review, - ) + tutor_load_template_from_custom_path( + $template_path . 'recent-student-review-item.php', + $review, + false ); ?>
+ diff --git a/templates/dashboard/instructor/home/recent-student-review-item.php b/templates/dashboard/instructor/home/recent-student-review-item.php new file mode 100644 index 0000000000..bbf24b1e88 --- /dev/null +++ b/templates/dashboard/instructor/home/recent-student-review-item.php @@ -0,0 +1,80 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +use TUTOR\Icon; +use Tutor\Components\Avatar; +use Tutor\Components\Constants\Size; + +$review = $data; +?> + +
+
+ +
+ src( $review['user']['avatar'] )->initials( $review['user']['name'] )->size( Size::SIZE_48 )->render(); ?> +
+ + +
+ +
+
+
+ +
+
+ + +
+
+ + +
+ $review['rating'] ?? 0, + 'wrapper_class' => 'tutor-ratings-stars tutor-flex tutor-items-center tutor-gap-2', + 'icon_class' => 'tutor-icon-exception4', + ) + ); + ?> +
+
+ + +
+ +
+ + +
+ + +
+
+
+
From 8cdb1106310a60033eb1ad63f375008a889a2866 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Wed, 21 Jan 2026 06:13:46 +0600 Subject: [PATCH 060/109] upcoming live task --- templates/dashboard/instructor/home.php | 81 ++++++++++++++----------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 6ca701c11c..03708c9ee4 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -192,44 +192,53 @@ function ( $course ) { $top_courses ); - - $upcoming_tasks = array( - array( - 'name' => 'Complete Web Development Bootcamp', - 'date' => '2022-01-01 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor_assignments', - 'meta_info' => 'Web Dev 101', - ), - array( - 'name' => 'Live Q&A: React Hooks', - 'date' => '2022-01-02 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor-google-meet', - 'meta_info' => '67 registered', - ), - array( - 'name' => 'Quiz Closes: Python Functions', - 'date' => '2022-01-03 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor_quiz', - 'meta_info' => 'Python Basics', - ), - array( - 'name' => 'Live Q&A: Python Functions', - 'date' => '2022-01-04 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor_zoom_meeting', - 'meta_info' => '67 registered', - ), +$upcoming_live_tasks = get_posts( array( + 'post_type' => array(tutor()->zoom_post_type, tutor()->meet_post_type), + 'post_status' => 'publish', + 'date_query' => array( array( - 'name' => 'Lesson Closes: Python Functions', - 'date' => '2022-01-05 10:00 AM', - 'url' => '#', - 'post_type' => 'lesson', - 'meta_info' => 'Python Basics', + 'after' => current_time( 'mysql' ), ), - ); + ), +)); + +$upcoming_tasks = array( + array( + 'name' => 'Complete Web Development Bootcamp', + 'date' => '2022-01-01 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor_assignments', + 'meta_info' => 'Web Dev 101', + ), + array( + 'name' => 'Live Q&A: React Hooks', + 'date' => '2022-01-02 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor-google-meet', + 'meta_info' => '67 registered', + ), + array( + 'name' => 'Quiz Closes: Python Functions', + 'date' => '2022-01-03 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor_quiz', + 'meta_info' => 'Python Basics', + ), + array( + 'name' => 'Live Q&A: Python Functions', + 'date' => '2022-01-04 10:00 AM', + 'url' => '#', + 'post_type' => 'tutor_zoom_meeting', + 'meta_info' => '67 registered', + ), + array( + 'name' => 'Lesson Closes: Python Functions', + 'date' => '2022-01-05 10:00 AM', + 'url' => '#', + 'post_type' => 'lesson', + 'meta_info' => 'Python Basics', + ), +); // @todo will be added later. // $recent_activity = array( From b84bd081337a46d131c9cd3640e7ae78379e26da Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 21 Jan 2026 10:33:24 +0600 Subject: [PATCH 061/109] Code Format --- classes/Instructor.php | 54 ++--- templates/dashboard/instructor/home.php | 287 ++++++++++++------------ 2 files changed, 173 insertions(+), 168 deletions(-) diff --git a/classes/Instructor.php b/classes/Instructor.php index 9fddf6eb8f..842ff8228c 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -617,7 +617,7 @@ public static function get_instructor_total_students_by_date_range( $start_date, * Get course completion distribution data for a specific instructor. * * @since 4.0.0 - * + * * @param array $instructor_course_ids Optional list of course IDs. * * @return array { @@ -643,9 +643,9 @@ public static function get_course_completion_distribution_data_by_instructor( $i ); $cancel_statuses = array( 'cancel', 'canceled', 'cancelled' ); - $post_statuses = array_merge( $cancel_statuses, array( 'completed' ) ); + $post_statuses = array_merge( $cancel_statuses, array( 'completed' ) ); - if (empty($instructor_course_ids)){ + if ( empty( $instructor_course_ids ) ) { return $counts; } @@ -692,16 +692,16 @@ public static function get_course_completion_distribution_data_by_instructor( $i * * @since 4.0.0 * - * @param int $instructor_id Instructor user ID. + * @param int $instructor_id Instructor user ID. * @param array $args { * Optional query arguments. * * @type string $start_date Optional start date (Y-m-d). * @type string $end_date Optional end date (Y-m-d). * @type string $order_by Sorting criteria. Accepts 'revenue' or 'student'. - *} + * } - * @param int $limit Maximum number of courses to return. Default 4. + * @param int $limit Maximum number of courses to return. Default 4. * * @return array List of course objects containing: * - course_id (int) @@ -716,9 +716,9 @@ public static function get_top_performing_courses_by_instructor( $instructor_id, global $wpdb; $start_date = $args['start_date'] ?? null; - $end_date = $args['end_date'] ?? null; - $order_by = 'revenue' === $args['order_by'] ? 'total_revenue' : 'total_student'; - + $end_date = $args['end_date'] ?? null; + $order_by = 'revenue' === $args['order_by'] ? 'total_revenue' : 'total_student'; + $complete_status = tutor_utils()->get_earnings_completed_statuses(); $amount_type = current_user_can( 'administrator' ) ? 'earnings.admin_amount' : 'earnings.instructor_amount'; @@ -729,21 +729,21 @@ public static function get_top_performing_courses_by_instructor( $instructor_id, THEN ( earnings.course_price_grand_total - orders.tax_amount ) * ( $amount_rate/100 ) ELSE $amount_type END"; - + $earning_where_clause = array( - 'earnings.user_id' => $instructor_id, - 'earnings.order_status' => array('IN', $complete_status), - ); - - $enrollment_where_clause = array('post_type' => 'tutor_enrolled'); + 'earnings.user_id' => $instructor_id, + 'earnings.order_status' => array( 'IN', $complete_status ), + ); + + $enrollment_where_clause = array( 'post_type' => 'tutor_enrolled' ); - if (!empty($start_date) && !empty($end_date)) { + if ( ! empty( $start_date ) && ! empty( $end_date ) ) { $earning_where_clause['earnings.created_at'] = array( 'BETWEEN', array( $start_date, $end_date ) ); - $enrollment_where_clause['post_date'] = array( 'BETWEEN', array( $start_date, $end_date ) ); + $enrollment_where_clause['post_date'] = array( 'BETWEEN', array( $start_date, $end_date ) ); } - $earning_where_clause = QueryHelper::prepare_where_clause($earning_where_clause); - $enrollment_where_clause = QueryHelper::prepare_where_clause($enrollment_where_clause); + $earning_where_clause = QueryHelper::prepare_where_clause( $earning_where_clause ); + $enrollment_where_clause = QueryHelper::prepare_where_clause( $enrollment_where_clause ); $earnings_sql = "SELECT earnings.course_id, @@ -754,14 +754,14 @@ public static function get_top_performing_courses_by_instructor( $instructor_id, GROUP BY earnings.course_id"; $enrollment_sql = QueryHelper::prepare_raw_query( - "SELECT - post_parent AS course_id, - COUNT(ID) AS total_student - FROM {$wpdb->posts} - WHERE {$enrollment_where_clause} - GROUP BY post_parent", - array() - ); + "SELECT + post_parent AS course_id, + COUNT(ID) AS total_student + FROM {$wpdb->posts} + WHERE {$enrollment_where_clause} + GROUP BY post_parent", + array() + ); $result = $wpdb->get_results( $wpdb->prepare( diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 03708c9ee4..d43f5ae370 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -12,10 +12,10 @@ use TUTOR\Instructor; use TUTOR_REPORT\Analytics; use Tutor\Models\CourseModel; +use Tutor\Helpers\QueryHelper; use Tutor\Components\DateFilter; use Tutor\Components\InputField; use Tutor\Components\Constants\InputType; -use Tutor\Helpers\QueryHelper; $sortable_sections = tutor_utils()->get_instructor_home_sortable_section(); $sortable_sections_defaults = array_reduce( @@ -44,7 +44,7 @@ function ( $carry, $section ) { ) )->posts; -$template_path = tutor()->path . 'templates/dashboard/instructor/home/'; +$template_path = tutor()->path . 'templates/dashboard/instructor/home/'; $user = wp_get_current_user(); $start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; $end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; @@ -174,12 +174,12 @@ function ( $carry, $section ) { // ); // Top Performing Courses. -$args = array( +$args = array( 'start_date' => $start_date, - 'end_date' => $end_date, - 'order_by' => Input::get( 'type', 'revenue' ), + 'end_date' => $end_date, + 'order_by' => Input::get( 'type', 'revenue' ), ); -$top_courses = Instructor::get_top_performing_courses_by_instructor( $user->ID, $args); +$top_courses = Instructor::get_top_performing_courses_by_instructor( $user->ID, $args ); $top_performing_courses = array_map( function ( $course ) { return array( @@ -192,15 +192,17 @@ function ( $course ) { $top_courses ); -$upcoming_live_tasks = get_posts( array( - 'post_type' => array(tutor()->zoom_post_type, tutor()->meet_post_type), - 'post_status' => 'publish', - 'date_query' => array( - array( - 'after' => current_time( 'mysql' ), +$upcoming_live_tasks = get_posts( + array( + 'post_type' => array( tutor()->zoom_post_type, tutor()->meet_post_type ), + 'post_status' => 'publish', + 'date_query' => array( + array( + 'after' => current_time( 'mysql' ), + ), ), - ), -)); + ) +); $upcoming_tasks = array( array( @@ -242,127 +244,130 @@ function ( $course ) { // @todo will be added later. // $recent_activity = array( -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), +// array( +// 'course_name' => 'Complete Web Development Bootcamp', +// 'course_url' => '#', +// 'date' => '2022-01-01 10:00 AM', +// 'meta' => 'enrolled in', +// 'user' => array( +// 'name' => 'John Doe', +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// ), +// ), // ); // Recent Reviews. -$review_where = array('comment_post_ID' => array( 'IN', $instructor_course_ids)); -if (!empty($start_date) && !empty($end_date)) { - $review_where['comment_date'] = array('BETWEEN', array( $start_date, $end_date )); +$review_where = array( 'comment_post_ID' => array( 'IN', $instructor_course_ids ) ); +if ( ! empty( $start_date ) && ! empty( $end_date ) ) { + $review_where['comment_date'] = array( 'BETWEEN', array( $start_date, $end_date ) ); } -$review_args = array('where' => QueryHelper::prepare_where_clause($review_where)); -$reviews = Analytics::get_reviews(3,$review_args); -$recent_reviews = array_map(function($review){ - return array( - 'user' => array( - 'name' => $review->display_name, - 'avatar' => get_avatar_url($review->user_id), - ), - 'course_name' => get_the_title($review->comment_post_ID), - 'date' => $review->comment_date, - 'rating' => $review->rating, - 'review_text' => $review->comment_content - ); -}, $reviews); +$review_args = array( 'where' => QueryHelper::prepare_where_clause( $review_where ) ); +$reviews = Analytics::get_reviews( 3, $review_args ); +$recent_reviews = array_map( + function ( $review ) { + return array( + 'user' => array( + 'name' => $review->display_name, + 'avatar' => get_avatar_url( $review->user_id ), + ), + 'course_name' => get_the_title( $review->comment_post_ID ), + 'date' => $review->comment_date, + 'rating' => $review->rating, + 'review_text' => $review->comment_content, + ); + }, + $reviews +); ?>
+
- + $item, - // ) + // 'demo-components.dashboard.components.instructor.home.recent-activity-item', + // array( + // 'item' => $item, + // ) // ); ?> - +
-->
- +
From 35e80b028a332b9bf4dfa98e583b4fc7138197b7 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 21 Jan 2026 15:18:11 +0600 Subject: [PATCH 062/109] upcoming live classes --- classes/Utils.php | 2 +- templates/dashboard/instructor/home.php | 201 +++++++++++------- .../instructor/home/upcoming-task-item.php | 60 ++++++ 3 files changed, 185 insertions(+), 78 deletions(-) create mode 100644 templates/dashboard/instructor/home/upcoming-task-item.php diff --git a/classes/Utils.php b/classes/Utils.php index 00fb64659a..43f1b60b3e 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -8555,7 +8555,7 @@ public function is_tutor_frontend_dashboard( $subpage = null ) { if ( $wp_query->is_page ) { $dashboard_page = $this->array_get( 'tutor_dashboard_page', $wp_query->query_vars ); - $subpage_parts = explode( '/', $subpage, 2 ); + $subpage_parts = $subpage ? explode( '/', $subpage, 2 ) : array(); if ( isset( $subpage_parts[1] ) ) { $dashboard_subpage = $this->array_get( 'tutor_dashboard_sub_page', $wp_query->query_vars ); return $dashboard_page == $subpage_parts[0] && $dashboard_subpage == $subpage_parts[1]; diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index d43f5ae370..7a08184a05 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -17,6 +17,7 @@ use Tutor\Components\InputField; use Tutor\Components\Constants\InputType; + $sortable_sections = tutor_utils()->get_instructor_home_sortable_section(); $sortable_sections_defaults = array_reduce( $sortable_sections, @@ -36,16 +37,19 @@ function ( $carry, $section ) { array() ); +$upcoming_tasks = array(); +$get_upcoming_live_tasks = array(); + +$user = wp_get_current_user(); $instructor_course_ids = CourseModel::get_courses_by_args( array( - 'post_author' => $instructor_id, + 'post_author' => $user->ID, 'posts_per_page' => -1, 'fields' => 'ids', ) )->posts; $template_path = tutor()->path . 'templates/dashboard/instructor/home/'; -$user = wp_get_current_user(); $start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; $end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; $previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); @@ -192,55 +196,97 @@ function ( $course ) { $top_courses ); -$upcoming_live_tasks = get_posts( - array( - 'post_type' => array( tutor()->zoom_post_type, tutor()->meet_post_type ), - 'post_status' => 'publish', - 'date_query' => array( - array( - 'after' => current_time( 'mysql' ), +if ( empty( $start_date ) && empty( $end_date ) ) { + $get_upcoming_live_tasks = get_posts( + array( + 'post_type' => array( tutor()->zoom_post_type, tutor()->meet_post_type ), + 'post_status' => 'publish', + 'post_author' => $user->ID, + 'numberposts' => 5, + 'meta_query' => array( + array( + 'key' => array( '_tutor_zm_start_datetime', 'tutor-google-meet-start-datetime' ), + 'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ), + 'compare' => '>=', + 'type' => 'DATETIME', + ), ), - ), - ) -); + ) + ); -$upcoming_tasks = array( - array( - 'name' => 'Complete Web Development Bootcamp', - 'date' => '2022-01-01 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor_assignments', - 'meta_info' => 'Web Dev 101', - ), - array( - 'name' => 'Live Q&A: React Hooks', - 'date' => '2022-01-02 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor-google-meet', - 'meta_info' => '67 registered', - ), - array( - 'name' => 'Quiz Closes: Python Functions', - 'date' => '2022-01-03 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor_quiz', - 'meta_info' => 'Python Basics', - ), - array( - 'name' => 'Live Q&A: Python Functions', - 'date' => '2022-01-04 10:00 AM', - 'url' => '#', - 'post_type' => 'tutor_zoom_meeting', - 'meta_info' => '67 registered', - ), - array( - 'name' => 'Lesson Closes: Python Functions', - 'date' => '2022-01-05 10:00 AM', - 'url' => '#', - 'post_type' => 'lesson', - 'meta_info' => 'Python Basics', - ), -); + if ( ! empty( $get_upcoming_live_tasks ) ) { + $upcoming_tasks = array_map( + function ( $task ) { + + switch ( $task->post_type ) { + case tutor()->zoom_post_type: + $meta_key = '_tutor_zm_start_datetime'; + $url = json_decode( get_post_meta( $task->ID, '_tutor_zm_data' )[0] )->join_url ?? ''; + break; + + case tutor()->meet_post_type: + $meta_key = 'tutor-google-meet-start-datetime'; + $url = get_post_meta( $task->ID, 'tutor-google-meet-link', true ); + break; + + default: + $meta_key = null; + $url = ''; + break; + } + + $start_date = get_post_meta( $task->ID, $meta_key, true ); + + return array( + 'name' => $task->post_title, + 'date' => wp_date( 'Y-m-d h:i A', strtotime( $start_date ) ), + 'url' => $url, + 'post_type' => $task->post_type, + 'meta_info' => $task->post_content, + ); + }, + $get_upcoming_live_tasks + ); + } +} + +// $upcoming_tasks = array( +// array( +// 'name' => 'Complete Web Development Bootcamp', +// 'date' => '2022-01-01 10:00 AM', +// 'url' => '#', +// 'post_type' => 'tutor_assignments', +// 'meta_info' => 'Web Dev 101', +// ), +// array( +// 'name' => 'Live Q&A: React Hooks', +// 'date' => '2022-01-02 10:00 AM', +// 'url' => '#', +// 'post_type' => 'tutor-google-meet', +// 'meta_info' => '67 registered', +// ), +// array( +// 'name' => 'Quiz Closes: Python Functions', +// 'date' => '2022-01-03 10:00 AM', +// 'url' => '#', +// 'post_type' => 'tutor_quiz', +// 'meta_info' => 'Python Basics', +// ), +// array( +// 'name' => 'Live Q&A: Python Functions', +// 'date' => '2022-01-04 10:00 AM', +// 'url' => '#', +// 'post_type' => 'tutor_zoom_meeting', +// 'meta_info' => '67 registered', +// ), +// array( +// 'name' => 'Lesson Closes: Python Functions', +// 'date' => '2022-01-05 10:00 AM', +// 'url' => '#', +// 'post_type' => 'lesson', +// 'meta_info' => 'Python Basics', +// ), +// ); // @todo will be added later. // $recent_activity = array( @@ -547,34 +593,35 @@ class="tutor-dashboard-home-card"
-
- -
-
- -
+ + +
+ +
+
+ +
-
- - $item, - ) - ); - ?> - +
+ + + +
-
- - - + + -
+
--> +
+ diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php new file mode 100644 index 0000000000..3a3e0cbf5c --- /dev/null +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -0,0 +1,60 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +use TUTOR\Icon; + +$item = $data; +$get_icon_by_post_type = function ( $post_type ) { + switch ( $post_type ) { + case 'tutor_assignments': + return Icon::ASSIGNMENT; + case 'tutor-google-meet': + return Icon::GOOGLE_MEET; + case 'tutor_quiz': + return Icon::QUIZ; + case 'tutor_zoom_meeting': + return Icon::ZOOM; + case 'lesson': + return Icon::LESSON; + } +}; + +?> + +
+
+ render_svg_icon( $get_icon_by_post_type( $item['post_type'] ) ); ?> +
+
+
+ + + + + + + + + + + +
+
+ +
+
+ +
+ + + render_svg_icon( Icon::CHEVRON_RIGHT_2 ); ?> + +
+
From 616bf3ca3328d3ad286b7a0dfea87b32d08e55b3 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 21 Jan 2026 15:22:02 +0600 Subject: [PATCH 063/109] removed unused code --- templates/dashboard/dashboard.php | 262 ------------------------------ 1 file changed, 262 deletions(-) diff --git a/templates/dashboard/dashboard.php b/templates/dashboard/dashboard.php index 47e44d8bb4..7a61167297 100644 --- a/templates/dashboard/dashboard.php +++ b/templates/dashboard/dashboard.php @@ -134,265 +134,3 @@ function ( $data ) { -
-
- get_completed_courses_ids_by_user(); - $total_students = tutor_utils()->get_total_students_by_instructor( $user_id ); - $my_courses = CourseModel::get_courses_by_instructor( $user_id, CourseModel::STATUS_PUBLISH ); - $earning_sum = WithdrawModel::get_withdraw_summary( $user_id ); - $active_courses = CourseModel::get_active_courses_by_user( $user_id ); - - $enrolled_course_count = $enrolled_course ? $enrolled_course->post_count : 0; - $completed_course_count = count( $completed_courses ); - $active_course_count = is_object( $active_courses ) && $active_courses->have_posts() ? $active_courses->post_count : 0; - - $status_translations = array( - 'publish' => __( 'Published', 'tutor' ), - 'pending' => __( 'Pending', 'tutor' ), - 'trash' => __( 'Trash', 'tutor' ), - ); - - ?> -
-
-
-
- - - -
-
-
-
-
-
- -
-
-
- - - -
-
-
-
-
-
- -
-
-
- - - -
-
-
-
-
-
- - instructor_role ) ) : - ?> -
-
-
- - - -
-
-
-
-
-
- -
-
-
- - - -
-
-
-
-
-
- -
-
-
- - - -
tutor_price( $earning_sum->total_income ) ); ?>
-
-
tutor_price( $earning_sum->total_income ) ); ?>
-
-
-
- -
-
- -url . 'assets/images/placeholder.svg'; -$courses_in_progress = CourseModel::get_active_courses_by_user( get_current_user_id() ); -?> - -have_posts() ) : ?> -
-
- -
- have_posts() ) : - $courses_in_progress->the_post(); - $tutor_course_img = get_tutor_course_thumbnail_src(); - $course_rating = tutor_utils()->get_course_rating( get_the_ID() ); - $course_progress = tutor_utils()->get_course_completed_percent( get_the_ID(), 0, true ); - $completed_number = 0 === (int) $course_progress['completed_count'] ? 1 : (int) $course_progress['completed_count']; - ?> -
-
-
-
- <?php the_title(); ?> -
-
- -
-
- -
- star_rating_generator( $course_rating->rating_avg ); ?> -
- rating_avg, 2 ) ); ?> -
-
- - -
- -
- -
- - - - - - - - - - - -
- -
-
-
-
- -
- - - - - -
-
-
-
- -
-
- - -
- - -get_courses_for_instructors( get_current_user_id() ); - -if ( count( $instructor_course ) ) { - $course_badges = array( - 'publish' => 'success', - 'pending' => 'warning', - 'trash' => 'danger', - ); - - ?> - - -
-
- - - - - - - - - - - - count_enrolled_users_by_course( $course->ID ); - $course_status = isset( $status_translations[ $course->post_status ] ) ? $status_translations[ $course->post_status ] : __( $course->post_status, 'tutor' ); //phpcs:ignore - $course_rating = tutor_utils()->get_course_rating( $course->ID ); - $course_badge = isset( $course_badges[ $course->post_status ] ) ? $course_badges[ $course->post_status ] : 'dark'; - ?> - - - - - - - - - - - - - -
-
- From 2a051dfa54b1a72cfbb5aa32fce4e6cb03fb11fc Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 21 Jan 2026 16:18:08 +0600 Subject: [PATCH 064/109] Removed demo components --- classes/Instructor.php | 2 ++ templates/dashboard/instructor/home.php | 32 ++++++++--------- .../home/course-completion-chart.php | 35 +++++++++++++++++++ .../instructor/home/overview-chart.php | 33 +++++++++++++++++ .../home/recent-student-review-item.php | 1 - .../home/top-performing-course-filter.php | 3 -- .../home/top-performing-course-item.php | 2 -- .../instructor/home/upcoming-task-item.php | 1 - 8 files changed, 84 insertions(+), 25 deletions(-) create mode 100644 templates/dashboard/instructor/home/course-completion-chart.php create mode 100644 templates/dashboard/instructor/home/overview-chart.php diff --git a/classes/Instructor.php b/classes/Instructor.php index 842ff8228c..ef0c831b3a 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -666,6 +666,8 @@ public static function get_course_completion_distribution_data_by_instructor( $i return $counts; } + $counts['enrolled'] = count( $enrollments ); + foreach ( $enrollments as $enrollment ) { if ( in_array( $enrollment->post_status, $cancel_statuses, true ) ) { diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 7a08184a05..04a01227ab 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -49,7 +49,7 @@ function ( $carry, $section ) { ) )->posts; -$template_path = tutor()->path . 'templates/dashboard/instructor/home/'; + $start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; $end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; $previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); @@ -485,7 +485,7 @@ class="tutor-flex tutor-gap-5"
isset( $card['variation'] ) ? $card['variation'] : 'enrolled', 'card_title' => isset( $card['title'] ) ? $card['title'] : '', @@ -504,7 +504,7 @@ class="tutor-flex tutor-gap-5" $overview_chart_data, ) @@ -519,7 +519,7 @@ class="tutor-flex tutor-gap-6" $course_completion_data, ) @@ -569,10 +569,9 @@ class="tutor-dashboard-home-card" ), 'selected' => Input::get( 'type', 'revenue' ), ); - tutor_load_template_from_custom_path( - $template_path . 'top-performing-course-filter.php', + tutor_load_template( + 'dashboard.instructor.home.top-performing-course-filter', $data, - false ); ?>
@@ -580,13 +579,12 @@ class="tutor-dashboard-home-card"
$item ) : ?> $item_key, 'item' => $item, ), - false ) ?> @@ -609,10 +607,9 @@ class="tutor-flex tutor-gap-6"
$item ) ); ?> @@ -655,10 +652,9 @@ class="tutor-dashboard-home-card"
$review ), ); ?> diff --git a/templates/dashboard/instructor/home/course-completion-chart.php b/templates/dashboard/instructor/home/course-completion-chart.php new file mode 100644 index 0000000000..d1f5eab620 --- /dev/null +++ b/templates/dashboard/instructor/home/course-completion-chart.php @@ -0,0 +1,35 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +?> + + +
+
+ +
+ + + +
+ $value ) : ?> +
+
+
+ +
+
+ +
+
+
+ +
+
diff --git a/templates/dashboard/instructor/home/overview-chart.php b/templates/dashboard/instructor/home/overview-chart.php new file mode 100644 index 0000000000..bfa6d46015 --- /dev/null +++ b/templates/dashboard/instructor/home/overview-chart.php @@ -0,0 +1,33 @@ + + * @link https://themeum.com + * @since 4.0.0 + */ + +?> + + +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
diff --git a/templates/dashboard/instructor/home/recent-student-review-item.php b/templates/dashboard/instructor/home/recent-student-review-item.php index bbf24b1e88..a65c9e0192 100644 --- a/templates/dashboard/instructor/home/recent-student-review-item.php +++ b/templates/dashboard/instructor/home/recent-student-review-item.php @@ -12,7 +12,6 @@ use Tutor\Components\Avatar; use Tutor\Components\Constants\Size; -$review = $data; ?>
diff --git a/templates/dashboard/instructor/home/top-performing-course-filter.php b/templates/dashboard/instructor/home/top-performing-course-filter.php index 65dd523c85..21717c4dc5 100644 --- a/templates/dashboard/instructor/home/top-performing-course-filter.php +++ b/templates/dashboard/instructor/home/top-performing-course-filter.php @@ -11,9 +11,6 @@ use TUTOR\Icon; -$options = isset( $data['options'] ) ? $data['options'] : array(); -$selected = isset( $data['selected'] ) ? $data['selected'] : ''; - ?>
diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php index 3a3e0cbf5c..35ad82075b 100644 --- a/templates/dashboard/instructor/home/upcoming-task-item.php +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -10,7 +10,6 @@ use TUTOR\Icon; -$item = $data; $get_icon_by_post_type = function ( $post_type ) { switch ( $post_type ) { case 'tutor_assignments': From 7a096d6b7efeb85de1ce8e7d9de9dfb8147ddd78 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 21 Jan 2026 17:43:18 +0600 Subject: [PATCH 065/109] condition added for empty data --- templates/dashboard/instructor/home.php | 72 +++++++++++++------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 04a01227ab..6ea4859996 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -550,46 +550,48 @@ class="tutor-flex tutor-gap-6"
-
-
-
- -
- - - array( - 'revenue' => __( 'Revenue', 'tutor' ), - 'student' => __( 'Student', 'tutor' ), - ), - 'selected' => Input::get( 'type', 'revenue' ), - ); - tutor_load_template( - 'dashboard.instructor.home.top-performing-course-filter', - $data, - ); - ?> -
+ +
+
+
+ +
-
- $item ) : ?> + $item_key, - 'item' => $item, + $data = array( + 'options' => array( + 'revenue' => __( 'Revenue', 'tutor' ), + 'student' => __( 'Student', 'tutor' ), ), - ) + 'selected' => Input::get( 'type', 'revenue' ), + ); + tutor_load_template( + 'dashboard.instructor.home.top-performing-course-filter', + $data, + ); ?> - +
+ +
+ $item ) : ?> + $item_key, + 'item' => $item, + ), + ) + ?> + +
-
+ From 2019981f1914c7e9cafd58bc554403fc3facc3fd Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 21 Jan 2026 18:05:51 +0600 Subject: [PATCH 066/109] Upcoming task new design --- .../scss/frontend/dashboard/instructor/dashboard.scss | 7 +++++++ templates/dashboard/instructor/home.php | 1 - .../dashboard/instructor/home/upcoming-task-item.php | 11 ++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/assets/src/scss/frontend/dashboard/instructor/dashboard.scss b/assets/src/scss/frontend/dashboard/instructor/dashboard.scss index 9e5003e89c..e8f64189bc 100644 --- a/assets/src/scss/frontend/dashboard/instructor/dashboard.scss +++ b/assets/src/scss/frontend/dashboard/instructor/dashboard.scss @@ -226,6 +226,13 @@ color: $tutor-text-brand; width: fit-content; } + + &-badge { + @include tutor-flex(row, center); + padding: $tutor-spacing-2 $tutor-spacing-3; + background: $tutor-brand-200; + border-radius: $tutor-radius-full; + } } &-activity { diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 6ea4859996..90e3c12076 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -242,7 +242,6 @@ function ( $task ) { 'date' => wp_date( 'Y-m-d h:i A', strtotime( $start_date ) ), 'url' => $url, 'post_type' => $task->post_type, - 'meta_info' => $task->post_content, ); }, $get_upcoming_live_tasks diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php index 35ad82075b..0baca8bea9 100644 --- a/templates/dashboard/instructor/home/upcoming-task-item.php +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -29,7 +29,7 @@
- render_svg_icon( $get_icon_by_post_type( $item['post_type'] ) ); ?> + render_svg_icon( Icon::VIDEO_CAMERA ); ?>
@@ -48,8 +48,13 @@
-
- +
+ render_svg_icon( $get_icon_by_post_type( $item['post_type'] ) ) . + __( 'Live Session', 'tutor' ) + ); + ?>
From 416ac8cd78b55cfd62c8db510f287b681e77223e Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Thu, 22 Jan 2026 03:38:31 +0600 Subject: [PATCH 067/109] Tutor instructor ( upcoming live class badge) --- .../frontend/dashboard/instructor/dashboard.scss | 16 +++++++++++----- .../instructor/home/upcoming-task-item.php | 16 +++++++++------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/assets/src/scss/frontend/dashboard/instructor/dashboard.scss b/assets/src/scss/frontend/dashboard/instructor/dashboard.scss index e8f64189bc..7ea026be42 100644 --- a/assets/src/scss/frontend/dashboard/instructor/dashboard.scss +++ b/assets/src/scss/frontend/dashboard/instructor/dashboard.scss @@ -227,11 +227,17 @@ width: fit-content; } - &-badge { - @include tutor-flex(row, center); - padding: $tutor-spacing-2 $tutor-spacing-3; - background: $tutor-brand-200; - border-radius: $tutor-radius-full; + &-live-tag { + position: relative; + margin-top: $tutor-spacing-5; + + &-badge { + @include tutor-flex(row, center, flex-start); + opacity: 1; + visibility: visible; + z-index: $tutor-z-positive; + @include tutor-transition((opacity, visibility)); + } } } diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php index 0baca8bea9..88ce17371f 100644 --- a/templates/dashboard/instructor/home/upcoming-task-item.php +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -48,13 +48,15 @@
-
- render_svg_icon( $get_icon_by_post_type( $item['post_type'] ) ) . - __( 'Live Session', 'tutor' ) - ); - ?> +
+
+ __( 'Live Session', 'tutor' ) ), + ); + ?> +
From 20266d82627c573a3bd9e087287837f405181695 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Thu, 22 Jan 2026 04:20:57 +0600 Subject: [PATCH 068/109] changed render_template functionalities --- classes/Utils.php | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 990667c181..4e70020a47 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10829,34 +10829,16 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U * * @param string $template Template file path or slug. * @param array $data Data to be passed to the template. - * @param string $type Template type. Allowed values: - * - include - * - custom_template - * - template - * @param bool $tutor_pro Whether to load Tutor Pro template. + * @param bool $once Whether the template should be loaded only once. + * Defaults to true. * * @return string Rendered template output. */ - public function render_template( $template, $data, $type, $tutor_pro = false ) { + public function render_template( $template, $data, $once = true ) { ob_start(); - switch ( $type ) { - case 'include': - include $template; - break; - - case 'custom_template': - tutor_load_template_from_custom_path( $template, $data ); - break; - - case 'template': - tutor_load_template( $template, $data, $tutor_pro ); - break; - - default: - break; - } + tutor_load_template_from_custom_path( $template, $data, $once ); return (string) ob_get_clean(); } From 2ad455b128536788b0fa11988fd2d357ac9d78bc Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 10:26:46 +0600 Subject: [PATCH 069/109] code formate --- templates/dashboard/instructor/home/upcoming-task-item.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php index 88ce17371f..1ca2a64203 100644 --- a/templates/dashboard/instructor/home/upcoming-task-item.php +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -50,9 +50,9 @@
- __( 'Live Session', 'tutor' ) ), ); ?> From 53644611594d1c44b324e87651656ac65ddfb48c Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 10:45:18 +0600 Subject: [PATCH 070/109] Icon Change in live class based on zoom and google meet --- .../home/recent-student-review-item.php | 21 ++++++++++--------- .../instructor/home/upcoming-task-item.php | 9 +++++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/templates/dashboard/instructor/home/recent-student-review-item.php b/templates/dashboard/instructor/home/recent-student-review-item.php index a65c9e0192..355823b0f4 100644 --- a/templates/dashboard/instructor/home/recent-student-review-item.php +++ b/templates/dashboard/instructor/home/recent-student-review-item.php @@ -56,24 +56,25 @@
-
+ +
diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php index 1ca2a64203..19045514e4 100644 --- a/templates/dashboard/instructor/home/upcoming-task-item.php +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -15,11 +15,11 @@ case 'tutor_assignments': return Icon::ASSIGNMENT; case 'tutor-google-meet': - return Icon::GOOGLE_MEET; + return Icon::GOOGLE_MEET_COLORIZE; case 'tutor_quiz': return Icon::QUIZ; case 'tutor_zoom_meeting': - return Icon::ZOOM; + return Icon::ZOOM_COLORIZE; case 'lesson': return Icon::LESSON; } @@ -53,7 +53,10 @@ __( 'Live Session', 'tutor' ) ), + array( + 'text' => __( 'Live Session', 'tutor' ), + 'icon' => $get_icon_by_post_type( $item['post_type'] ), + ), ); ?>
From 1eac1283c965ef6ee4e3dd6510bb9d54b4ea8639 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 15:53:26 +0600 Subject: [PATCH 071/109] Removed 'no-esc' from esc() --- components/BaseComponent.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/components/BaseComponent.php b/components/BaseComponent.php index f40b3b569c..b112e66821 100644 --- a/components/BaseComponent.php +++ b/components/BaseComponent.php @@ -127,11 +127,6 @@ protected function render_attributes(): string { * @return string Escaped string. */ protected function esc( $value, $esc_fn = 'esc_html' ): string { - - if ( 'no_esc' === $esc_fn ) { - return $value; - } - return call_user_func( $esc_fn, $value ); } From 2b9662d348e1e864fdd31159ed393424a2371573 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 16:41:59 +0600 Subject: [PATCH 072/109] Replace function from utils to instructor class and also used DateTimeHelper --- classes/Instructor.php | 35 ++++++----- classes/Utils.php | 77 ++----------------------- templates/dashboard/instructor/home.php | 40 ++++++++++++- 3 files changed, 66 insertions(+), 86 deletions(-) diff --git a/classes/Instructor.php b/classes/Instructor.php index ef0c831b3a..f0812b92bc 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -10,10 +10,13 @@ namespace TUTOR; +defined( 'ABSPATH' ) || exit; + use DateTime; use DateInterval; use Tutor\Models\CourseModel; use Tutor\Helpers\QueryHelper; +use Tutor\Helpers\DateTimeHelper; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -446,7 +449,7 @@ public static function get_stat_card_comparison_subtitle( $diff = ! empty( $start_date ) && ! empty( $end_date ) ? $current_data - $previous_data : $previous_data; $symbol = $diff < 0 ? '-' : ( $diff > 0 ? '+' : '' ); - $diff = $price ? wp_kses_post( tutor_utils()->tutor_price( abs( $diff ) ) ) : abs( $diff ); + $diff = $price ? tutor_utils()->tutor_price( abs( $diff ) ) : abs( $diff ); $start = new DateTime( $start_date ); $end = new DateTime( $end_date ); @@ -473,17 +476,18 @@ public static function get_stat_card_comparison_subtitle( public static function get_comparison_period_label( string $start_date, string $end_date, int $days ): string { $time_zone = wp_timezone(); - $now = new DateTime( 'now', $time_zone ); - $today = wp_date( 'Y-m-d', null, $time_zone ); + $format = DateTimeHelper::FORMAT_DATE; + $now = DateTimeHelper::now()->set_timezone( $time_zone ); + $today = $now->format( $format ); - $this_month_start = $now->modify( 'first day of this month' )->format( 'Y-m-d' ); - $this_month_end = $now->modify( 'last day of this month' )->format( 'Y-m-d' ); + $this_month_start = $now->create( 'first day of this month' )->format( $format ); + $this_month_end = $now->create( 'last day of this month' )->format( $format ); - $last_month_start = $now->modify( 'first day of last month' )->format( 'Y-m-d' ); - $last_month_end = $now->modify( 'last day of last month' )->format( 'Y-m-d' ); + $last_month_start = $now->create( 'first day of last month' )->format( $format ); + $last_month_end = $now->create( 'last day of last month' )->format( $format ); - $last_year_start = $now->modify( 'first day of January last year' )->format( 'Y-m-d' ); - $last_year_end = $now->modify( 'last day of December last year' )->format( 'Y-m-d' ); + $last_year_start = $now->create( 'first day of January last year' )->format( $format ); + $last_year_end = $now->create( 'last day of December last year' )->format( $format ); if ( $start_date === $today && $end_date === $today ) { return __( 'today', 'tutor' ); @@ -537,11 +541,14 @@ public static function get_comparison_period_label( string $start_date, string $ */ public static function get_comparison_date_range( $selected_start_date, $selected_end_date ) { + $format = DateTimeHelper::FORMAT_DATE; + if ( empty( $selected_start_date ) && empty( $selected_end_date ) ) { - $now = new DateTime(); + + $now = DateTimeHelper::now(); return array( - 'previous_start_date' => $now->modify( 'first day of this month' )->format( 'Y-m-d' ), - 'previous_end_date' => $now->modify( 'last day of this month' )->format( 'Y-m-d' ), + 'previous_start_date' => $now->create( 'first day of this month' )->format( $format ), + 'previous_end_date' => $now->create( 'last day of this month' )->format( $format ), ); } @@ -549,8 +556,8 @@ public static function get_comparison_date_range( $selected_start_date, $selecte $end = new DateTime( $selected_end_date ); $days = $start->diff( $end )->days + 1; - $previous_start_date = $start->sub( DateInterval::createFromDateString( "$days days" ) )->format( 'Y-m-d' ); - $previous_end_date = $end->sub( DateInterval::createFromDateString( "$days days" ) )->format( 'Y-m-d' ); + $previous_start_date = $start->sub( DateInterval::createFromDateString( "$days days" ) )->format( $format ); + $previous_end_date = $end->sub( DateInterval::createFromDateString( "$days days" ) )->format( $format ); return array( 'previous_start_date' => $previous_start_date, diff --git a/classes/Utils.php b/classes/Utils.php index 09bf10773b..3553fa7e3c 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10800,31 +10800,28 @@ public function build_term_tree( $terms, $parent = 0 ) { * Render SVG icon * * @since 3.7.0 - * @since 4.0.0 added $return parameter. * * @param string $name Icon name. * @param integer $width Icon width. * @param integer $height Icon height. * @param array $attributes Custom attributes. - * @param bool $return Whether to return the SVG markup instead of echoing it. - * Default false (echo). * - * @return string|null Returns the SVG markup when `$return` is true, otherwise null. + * @return void */ - public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array(), $return = false ): ?string { + public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = array() ){ $icon_path = tutor()->path . 'assets/icons/' . $name . '.svg'; if ( ! file_exists( $icon_path ) ) { - return null; + return; } $svg = file_get_contents( $icon_path ); if ( ! $svg ) { - return null; + return; } preg_match( '/]*viewBox="([^"]+)"[^>]*>(.*?)<\/svg>/is', $svg, $matches ); if ( ! $matches ) { - return null; + return; } list( $svg_tag, $view_box, $inner_svg ) = $matches; @@ -10840,12 +10837,8 @@ public function render_svg_icon( $name, $width = 16, $height = 16, $attributes = $attr_string .= ' ' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"'; } - if ( ! $return ) { - printf( '%s', $attr_string, $inner_svg ); - return null; - } - return '' . $inner_svg . ''; + printf( '%s', $attr_string, $inner_svg ); } /** @@ -10884,64 +10877,6 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U return null; } - /** - * Get sortable sections for the instructor home dashboard. - * - * @since 4.0.0 - * - * @return array[] { - * List of instructor dashboard sections. - * - * @type array { - * @type string $id Unique section identifier. - * @type string $label Section display label (translated). - * @type bool $is_active Whether the section is enabled. - * @type int $order Display order of the section. - * } - * } - */ - public function get_instructor_home_sortable_section() { - return array( - array( - 'id' => 'current_stats', - 'label' => esc_html__( 'Current Stats', 'tutor' ), - 'is_active' => true, - 'order' => 0, - ), - array( - 'id' => 'overview_chart', - 'label' => esc_html__( 'Earning Over Time', 'tutor' ), - 'is_active' => true, - 'order' => 1, - ), - array( - 'id' => 'course_completion_and_leader', - 'label' => esc_html__( 'Course Completion and Leader', 'tutor' ), - 'is_active' => true, - 'order' => 2, - ), - array( - 'id' => 'top_performing_courses', - 'label' => esc_html__( 'Top Performing Courses', 'tutor' ), - 'is_active' => true, - 'order' => 3, - ), - array( - 'id' => 'upcoming_tasks_and_activity', - 'label' => esc_html__( 'Upcoming Tasks and Recent Activity', 'tutor' ), - 'is_active' => true, - 'order' => 4, - ), - array( - 'id' => 'recent_reviews', - 'label' => esc_html__( 'Recent Student Reviews', 'tutor' ), - 'is_active' => true, - 'order' => 6, - ), - ); - } - - /* Render a template and return its output as a string. * diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 90e3c12076..07fa3f090c 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -18,7 +18,45 @@ use Tutor\Components\Constants\InputType; -$sortable_sections = tutor_utils()->get_instructor_home_sortable_section(); +$sortable_sections = array( + array( + 'id' => 'current_stats', + 'label' => esc_html__( 'Current Stats', 'tutor' ), + 'is_active' => true, + 'order' => 0, + ), + array( + 'id' => 'overview_chart', + 'label' => esc_html__( 'Earning Over Time', 'tutor' ), + 'is_active' => true, + 'order' => 1, + ), + array( + 'id' => 'course_completion_and_leader', + 'label' => esc_html__( 'Course Completion and Leader', 'tutor' ), + 'is_active' => true, + 'order' => 2, + ), + array( + 'id' => 'top_performing_courses', + 'label' => esc_html__( 'Top Performing Courses', 'tutor' ), + 'is_active' => true, + 'order' => 3, + ), + array( + 'id' => 'upcoming_tasks_and_activity', + 'label' => esc_html__( 'Upcoming Tasks and Recent Activity', 'tutor' ), + 'is_active' => true, + 'order' => 4, + ), + array( + 'id' => 'recent_reviews', + 'label' => esc_html__( 'Recent Student Reviews', 'tutor' ), + 'is_active' => true, + 'order' => 6, + ), +); + $sortable_sections_defaults = array_reduce( $sortable_sections, function ( $carry, $section ) { From c00c7440fed681143d5c92192a0a589dbfb1ad49 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 17:12:49 +0600 Subject: [PATCH 073/109] Replace function from utils to course model --- classes/Utils.php | 105 ------------------------------------- models/CourseModel.php | 114 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 105 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 3553fa7e3c..8eb4b9b658 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10897,109 +10897,4 @@ public function render_template( $template, $data, $once = true ) { return (string) ob_get_clean(); } - - /** - * Get topic-wise progress data for a course and a student. - * - * @since 4.0.0 - * - * @param int $course_id Course ID. - * @param int $student_id Student ID. - * - * @return array[] Topic progress data. - */ - public function get_topic_progress_by_course_id( $course_id, $student_id ) { - - $topics_query = $this->get_topics( $course_id ); - - $topic_list = array(); - - if ( empty( $topics_query ) || ! $topics_query->have_posts() ) { - return $topic_list; - } - - foreach ( $topics_query->posts as $topic_post ) { - $topic_id = (int) $topic_post->ID; - - $topic = array( - 'topic_id' => $topic_id, - 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), - 'topic_title' => get_the_title( $topic_id ), - 'items' => array(), - 'topic_completed' => true, - 'topic_started' => false, - ); - - $contents_query = tutor_utils()->get_course_contents_by_topic( $topic_id, -1 ); - - if ( ! empty( $contents_query ) && $contents_query->have_posts() ) { - foreach ( $contents_query->posts as $content_post ) { - $post_id = (int) $content_post->ID; - $post_type = $content_post->post_type; - $is_completed = true; - - if ( 'tutor_quiz' === $post_type ) { - - $is_completed = (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ); - - $topic['items'][] = array( - 'type' => 'quiz', - 'id' => $post_id, - 'link' => esc_url( get_permalink( $post_id ) ), - 'title' => $content_post->post_title, - 'is_completed' => $is_completed, - 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), - 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), - ); - - } elseif ( 'tutor_assignments' === $post_type ) { - - $submitted_count = (int) tutor_utils()->get_submitted_assignment_count( $post_id, $student_id ); - $is_completed = $submitted_count > 0; - - $topic['items'][] = array( - 'type' => 'assignment', - 'id' => $post_id, - 'link' => esc_url( get_permalink( $post_id ) ), - 'title' => $content_post->post_title, - 'is_completed' => $is_completed, - ); - - } elseif ( 'tutor_zoom_meeting' === $post_type ) { // @todo Need to add more information. - $topic['items'][] = array( - 'type' => 'zoom_meeting', - 'id' => $post_id, - 'link' => esc_url( get_permalink( $post_id ) ), - ); - - } else { - $video = tutor_utils()->get_video_info( $post_id ); - $is_completed = (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ); - - $topic['items'][] = array( - 'type' => 'lesson', - 'id' => $post_id, - 'link' => esc_url( get_permalink( $post_id ) ), - 'title' => $content_post->post_title, - 'video' => $video, - 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', - 'is_completed' => $is_completed, - ); - } - - if ( ! $is_completed ) { - $topic['topic_completed'] = false; - } else { - $topic['topic_started'] = true; - } - } - } - - $topic_list[] = $topic; - } - - wp_reset_postdata(); - - return $topic_list; - } } diff --git a/models/CourseModel.php b/models/CourseModel.php index 6efa3c9e50..61365bf027 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -1411,4 +1411,118 @@ function ( int $post_id ) use ( $start_date, $end_date ): bool { return count( $filtered ); } + + /** + * Get topic-wise progress data for a course and a student. + * + * @since 4.0.0 + * + * @param int $course_id Course ID. + * @param int $student_id Student ID. + * + * @return array[] Topic progress data. + */ + public static function get_topic_progress_by_course_id( $course_id, $student_id ) { + + $topics_query = tutor_utils()->get_topics( $course_id ); + + $topic_list = array(); + + if ( empty( $topics_query ) || ! $topics_query->have_posts() ) { + return $topic_list; + } + + foreach ( $topics_query->posts as $topic_post ) { + $topic_id = (int) $topic_post->ID; + + $topic = array( + 'topic_id' => $topic_id, + 'topic_summary' => apply_filters( 'the_content', $topic_post->post_content ), + 'topic_title' => get_the_title( $topic_id ), + 'items' => array(), + 'topic_completed' => true, + 'topic_started' => false, + ); + + $contents_query = tutor_utils()->get_course_contents_by_topic( $topic_id, -1 ); + + if ( ! empty( $contents_query ) && $contents_query->have_posts() ) { + foreach ( $contents_query->posts as $content_post ) { + $post_id = (int) $content_post->ID; + $post_type = $content_post->post_type; + $is_completed = true; + + if ( 'tutor_quiz' === $post_type ) { + + $is_completed = (bool) tutor_utils()->has_attempted_quiz( $student_id, $post_id ); + + $topic['items'][] = array( + 'type' => 'quiz', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), + 'title' => $content_post->post_title, + 'is_completed' => $is_completed, + 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), + 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), + ); + + } elseif ( 'tutor_assignments' === $post_type ) { + + $submitted_count = (int) tutor_utils()->get_submitted_assignment_count( $post_id, $student_id ); + $is_completed = $submitted_count > 0; + + $topic['items'][] = array( + 'type' => 'assignment', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), + 'title' => $content_post->post_title, + 'is_completed' => $is_completed, + ); + + } elseif ( tutor()->zoom_post_type === $post_type ) { + $topic['items'][] = array( + 'type' => 'zoom_meeting', + 'id' => $post_id, + 'title' => $content_post->post_title, + 'link' => esc_url( get_permalink( $post_id ) ), + ); + + } elseif ( tutor()->meet_post_type === $post_type ) { + $topic['items'][] = array( + 'type' => 'google_meet', + 'id' => $post_id, + 'title' => $content_post->post_title, + 'link' => esc_url( get_permalink( $post_id ) ), + ); + + } else { + $video = tutor_utils()->get_video_info( $post_id ); + $is_completed = (bool) tutor_utils()->is_completed_lesson( $post_id, $student_id ); + + $topic['items'][] = array( + 'type' => 'lesson', + 'id' => $post_id, + 'link' => esc_url( get_permalink( $post_id ) ), + 'title' => $content_post->post_title, + 'video' => $video, + 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', + 'is_completed' => $is_completed, + ); + } + + if ( ! $is_completed ) { + $topic['topic_completed'] = false; + } else { + $topic['topic_started'] = true; + } + } + } + + $topic_list[] = $topic; + } + + wp_reset_postdata(); + + return $topic_list; + } } From 9ac9feb673abb8932be530a72ef4cae9e7320983 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 17:55:48 +0600 Subject: [PATCH 074/109] Replace and rename `render_template()` from Utils --- classes/Utils.php | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 8eb4b9b658..447f83c6f4 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -10876,25 +10876,4 @@ public function get_script_locale_data( string $filename, string $locale = 'en_U return null; } - - /* - Render a template and return its output as a string. - * - * @since 4.0.0 - * - * @param string $template Template file path or slug. - * @param array $data Data to be passed to the template. - * @param bool $once Whether the template should be loaded only once. - * Defaults to true. - * - * @return string Rendered template output. - */ - public function render_template( $template, $data, $once = true ) { - - ob_start(); - - tutor_load_template_from_custom_path( $template, $data, $once ); - - return (string) ob_get_clean(); - } } From e027c5ef4716c2fa4ae61a5a98156eb306bca16e Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Thu, 22 Jan 2026 18:02:30 +0600 Subject: [PATCH 075/109] Undo the previous commit --- models/OrderModel.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/OrderModel.php b/models/OrderModel.php index 2d04247f8a..e7f2efde29 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -1093,7 +1093,7 @@ public function get_discounts_by_user( int $user_id, string $period = '', $start $date_range_clause = ''; $period_clause = ''; $course_clause = ''; - $group_clause = ' GROUP BY MONTH(date_format) '; + $group_clause = ' GROUP BY DATE(date_format) '; $discount_clause = 'o.coupon_amount as total'; if ( $start_date && $end_date ) { From ab11b76bfe26bfe765098116624e761378dbe5e6 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 23 Jan 2026 11:55:19 +0600 Subject: [PATCH 076/109] Used Star Rating Component --- classes/Instructor.php | 8 ++-- templates/dashboard/elements/pagination.php | 2 +- .../instructor/analytics/star-rating.php | 45 ------------------- .../home/recent-student-review-item.php | 25 ++++------- 4 files changed, 12 insertions(+), 68 deletions(-) delete mode 100644 templates/dashboard/instructor/analytics/star-rating.php diff --git a/classes/Instructor.php b/classes/Instructor.php index f0812b92bc..7491c449cc 100644 --- a/classes/Instructor.php +++ b/classes/Instructor.php @@ -10,7 +10,9 @@ namespace TUTOR; -defined( 'ABSPATH' ) || exit; +if ( ! defined( 'ABSPATH' ) ) { + exit; +} use DateTime; use DateInterval; @@ -18,10 +20,6 @@ use Tutor\Helpers\QueryHelper; use Tutor\Helpers\DateTimeHelper; -if ( ! defined( 'ABSPATH' ) ) { - exit; -} - /** * Instructor class * diff --git a/templates/dashboard/elements/pagination.php b/templates/dashboard/elements/pagination.php index bfc1270ec7..5dc27bf97d 100644 --- a/templates/dashboard/elements/pagination.php +++ b/templates/dashboard/elements/pagination.php @@ -42,7 +42,7 @@ } if ( ( isset( $data['total_page'] ) && $data['total_page'] ) || ( isset( $data['total_items'] ) && $data['total_items'] ) ) : ?> -
@@ -59,20 +50,20 @@
From de84c3e899253114840882b8350ede156f693ad3 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 23 Jan 2026 12:59:55 +0600 Subject: [PATCH 077/109] added defined( 'ABSPATH' ) to Prevent direct access --- templates/dashboard/dashboard.php | 5 +- .../instructor/analytics/stat-card.php | 2 + templates/dashboard/instructor/home.php | 161 +++--------------- .../home/course-completion-chart.php | 1 + .../instructor/home/overview-chart.php | 2 + .../home/recent-student-review-item.php | 23 +-- .../home/top-performing-course-filter.php | 5 +- .../home/top-performing-course-item.php | 2 + .../instructor/home/upcoming-task-item.php | 17 +- 9 files changed, 45 insertions(+), 173 deletions(-) diff --git a/templates/dashboard/dashboard.php b/templates/dashboard/dashboard.php index 7a61167297..a339d8bc71 100644 --- a/templates/dashboard/dashboard.php +++ b/templates/dashboard/dashboard.php @@ -9,8 +9,7 @@ * @since 1.4.3 */ -use Tutor\Models\CourseModel; -use Tutor\Models\WithdrawModel; +defined( 'ABSPATH' ) || exit; if ( tutor_utils()->get_option( 'enable_profile_completion' ) ) { $profile_completion = tutor_utils()->user_profile_completion(); @@ -101,7 +100,7 @@ function ( $data ) {
diff --git a/templates/dashboard/instructor/analytics/stat-card.php b/templates/dashboard/instructor/analytics/stat-card.php index 1e99250551..59c0fb5d20 100644 --- a/templates/dashboard/instructor/analytics/stat-card.php +++ b/templates/dashboard/instructor/analytics/stat-card.php @@ -9,6 +9,8 @@ * @since 4.0.0 */ +defined( 'ABSPATH' ) || exit; + // Default values. $icon_size = $icon_size ?? 24; $variation = isset( $variation ) ? $variation : 'enrolled'; diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 07fa3f090c..82bf979a31 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -7,6 +7,9 @@ * @link https://themeum.com * @since 4.0.0 */ + +defined( 'ABSPATH' ) || exit; + use TUTOR\Icon; use TUTOR\Input; use TUTOR\Instructor; @@ -183,36 +186,24 @@ function ( $carry, $section ) { // @todo Will be added on later. // $leaderboard_data = array( -// array( -// 'name' => esc_html__( 'John Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// 'no_of_courses' => 10, -// 'completion_percentage' => 50, -// ), -// array( -// 'name' => esc_html__( 'Jane Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a042581f4e29026704d', -// 'no_of_courses' => 20, -// 'completion_percentage' => 30, -// ), -// array( -// 'name' => esc_html__( 'Bob Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826732d', -// 'no_of_courses' => 30, -// 'completion_percentage' => 70, -// ), -// array( -// 'name' => esc_html__( 'Alice Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826752d', -// 'no_of_courses' => 40, -// 'completion_percentage' => 10, -// ), -// array( -// 'name' => esc_html__( 'Charlie Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d823712d', -// 'no_of_courses' => 50, -// 'completion_percentage' => 40, -// ), +// array( +// 'name' => esc_html__( 'John Doe', 'tutor' ), +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// 'no_of_courses' => 10, +// 'completion_percentage' => 50, +// ), +// array( +// 'name' => esc_html__( 'Jane Doe', 'tutor' ), +// 'avatar' => 'https://i.pravatar.cc/300?u=a042581f4e29026704d', +// 'no_of_courses' => 20, +// 'completion_percentage' => 30, +// ), +// array( +// 'name' => esc_html__( 'Bob Doe', 'tutor' ), +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826732d', +// 'no_of_courses' => 30, +// 'completion_percentage' => 70, +// ), // ); // Top Performing Courses. @@ -287,44 +278,6 @@ function ( $task ) { } } -// $upcoming_tasks = array( -// array( -// 'name' => 'Complete Web Development Bootcamp', -// 'date' => '2022-01-01 10:00 AM', -// 'url' => '#', -// 'post_type' => 'tutor_assignments', -// 'meta_info' => 'Web Dev 101', -// ), -// array( -// 'name' => 'Live Q&A: React Hooks', -// 'date' => '2022-01-02 10:00 AM', -// 'url' => '#', -// 'post_type' => 'tutor-google-meet', -// 'meta_info' => '67 registered', -// ), -// array( -// 'name' => 'Quiz Closes: Python Functions', -// 'date' => '2022-01-03 10:00 AM', -// 'url' => '#', -// 'post_type' => 'tutor_quiz', -// 'meta_info' => 'Python Basics', -// ), -// array( -// 'name' => 'Live Q&A: Python Functions', -// 'date' => '2022-01-04 10:00 AM', -// 'url' => '#', -// 'post_type' => 'tutor_zoom_meeting', -// 'meta_info' => '67 registered', -// ), -// array( -// 'name' => 'Lesson Closes: Python Functions', -// 'date' => '2022-01-05 10:00 AM', -// 'url' => '#', -// 'post_type' => 'lesson', -// 'meta_info' => 'Python Basics', -// ), -// ); - // @todo will be added later. // $recent_activity = array( // array( @@ -357,76 +310,6 @@ function ( $task ) { // 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', // ), // ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), -// array( -// 'course_name' => 'Complete Web Development Bootcamp', -// 'course_url' => '#', -// 'date' => '2022-01-01 10:00 AM', -// 'meta' => 'enrolled in', -// 'user' => array( -// 'name' => 'John Doe', -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// ), -// ), // ); // Recent Reviews. @@ -567,7 +450,7 @@ class="tutor-flex tutor-gap-6" diff --git a/templates/dashboard/instructor/home/overview-chart.php b/templates/dashboard/instructor/home/overview-chart.php index bfa6d46015..eee71787a8 100644 --- a/templates/dashboard/instructor/home/overview-chart.php +++ b/templates/dashboard/instructor/home/overview-chart.php @@ -8,6 +8,8 @@ * @since 4.0.0 */ +defined( 'ABSPATH' ) || exit; + ?> diff --git a/templates/dashboard/instructor/home/recent-student-review-item.php b/templates/dashboard/instructor/home/recent-student-review-item.php index a85b663f4f..2e691272a9 100644 --- a/templates/dashboard/instructor/home/recent-student-review-item.php +++ b/templates/dashboard/instructor/home/recent-student-review-item.php @@ -8,6 +8,8 @@ * @since 4.0.0 */ +defined( 'ABSPATH' ) || exit; + use Tutor\Components\Avatar; use Tutor\Components\StarRating; use Tutor\Components\Constants\Size; @@ -45,27 +47,6 @@
- - - -
diff --git a/templates/dashboard/instructor/home/top-performing-course-filter.php b/templates/dashboard/instructor/home/top-performing-course-filter.php index 21717c4dc5..c2c2829c84 100644 --- a/templates/dashboard/instructor/home/top-performing-course-filter.php +++ b/templates/dashboard/instructor/home/top-performing-course-filter.php @@ -1,6 +1,6 @@
diff --git a/templates/dashboard/instructor/home/upcoming-task-item.php b/templates/dashboard/instructor/home/upcoming-task-item.php index 19045514e4..99179f9b41 100644 --- a/templates/dashboard/instructor/home/upcoming-task-item.php +++ b/templates/dashboard/instructor/home/upcoming-task-item.php @@ -8,7 +8,10 @@ * @since 4.0.0 */ +defined( 'ABSPATH' ) || exit; + use TUTOR\Icon; +use Tutor\Components\Badge; $get_icon_by_post_type = function ( $post_type ) { switch ( $post_type ) { @@ -25,6 +28,7 @@ } }; +$label = __( 'Live Session', 'tutor' ); ?>
@@ -50,14 +54,11 @@
- __( 'Live Session', 'tutor' ), - 'icon' => $get_icon_by_post_type( $item['post_type'] ), - ), - ); + icon( $get_icon_by_post_type( $item['post_type'] ) ) + ->label( $label ) + ->rounded() + ->render(); ?>
From 51e4be702117aa07c1ed207886a9fddc02be2514 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 23 Jan 2026 13:09:13 +0600 Subject: [PATCH 078/109] code formate --- templates/dashboard/instructor/home/overview-chart.php | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/dashboard/instructor/home/overview-chart.php b/templates/dashboard/instructor/home/overview-chart.php index eee71787a8..726abc6ce3 100644 --- a/templates/dashboard/instructor/home/overview-chart.php +++ b/templates/dashboard/instructor/home/overview-chart.php @@ -9,7 +9,6 @@ */ defined( 'ABSPATH' ) || exit; - ?> From a5577a907ae8c92aca527320589a7ddce68dccbd Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 23 Jan 2026 16:35:03 +0600 Subject: [PATCH 079/109] remove redundant code for course progress modal --- models/CourseModel.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/models/CourseModel.php b/models/CourseModel.php index 61365bf027..04e85412a4 100644 --- a/models/CourseModel.php +++ b/models/CourseModel.php @@ -10,6 +10,7 @@ namespace Tutor\Models; +use TUTOR\Icon; use TUTOR\Course; use Tutor\Ecommerce\Tax; use Tutor\Helpers\QueryHelper; @@ -1464,6 +1465,8 @@ public static function get_topic_progress_by_course_id( $course_id, $student_id 'is_completed' => $is_completed, 'time_limit' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_value' ), 'time_type' => tutor_utils()->get_quiz_option( $post_id, 'time_limit.time_type' ), + 'label' => __( 'Quiz', 'tutor' ), + 'icon' => Icon::QUIZ_2, ); } elseif ( 'tutor_assignments' === $post_type ) { @@ -1477,6 +1480,8 @@ public static function get_topic_progress_by_course_id( $course_id, $student_id 'link' => esc_url( get_permalink( $post_id ) ), 'title' => $content_post->post_title, 'is_completed' => $is_completed, + 'label' => __( 'Assignment', 'tutor' ), + 'icon' => Icon::BOOK_2, ); } elseif ( tutor()->zoom_post_type === $post_type ) { @@ -1485,6 +1490,8 @@ public static function get_topic_progress_by_course_id( $course_id, $student_id 'id' => $post_id, 'title' => $content_post->post_title, 'link' => esc_url( get_permalink( $post_id ) ), + 'label' => __( 'Live Class', 'tutor' ), + 'icon' => Icon::ZOOM, ); } elseif ( tutor()->meet_post_type === $post_type ) { @@ -1493,6 +1500,8 @@ public static function get_topic_progress_by_course_id( $course_id, $student_id 'id' => $post_id, 'title' => $content_post->post_title, 'link' => esc_url( get_permalink( $post_id ) ), + 'label' => __( 'Live Class', 'tutor' ), + 'icon' => Icon::GOOGLE_MEET, ); } else { @@ -1507,6 +1516,8 @@ public static function get_topic_progress_by_course_id( $course_id, $student_id 'video' => $video, 'video_play_time' => isset( $video->playtime ) ? $video->playtime : '', 'is_completed' => $is_completed, + 'label' => __( 'Reading', 'tutor' ), + 'icon' => Icon::COURSES, ); } From 43e0c415590885f872ad52242ddc8614a3a1dcb9 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Fri, 23 Jan 2026 16:55:11 +0600 Subject: [PATCH 080/109] Replaced variation value with icon --- assets/src/scss/frontend/dashboard/_stat-card.scss | 3 ++- templates/dashboard/instructor/analytics/stat-card.php | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/src/scss/frontend/dashboard/_stat-card.scss b/assets/src/scss/frontend/dashboard/_stat-card.scss index 791a1985d9..bd60f186de 100644 --- a/assets/src/scss/frontend/dashboard/_stat-card.scss +++ b/assets/src/scss/frontend/dashboard/_stat-card.scss @@ -112,7 +112,7 @@ } } - + &-passed, &-students { .tutor-stat-card-icon { color: $tutor-text-brand; @@ -123,6 +123,7 @@ } } + &-star-fill, &-reviews { .tutor-stat-card-icon { color: $tutor-warning-400; diff --git a/templates/dashboard/instructor/analytics/stat-card.php b/templates/dashboard/instructor/analytics/stat-card.php index 59c0fb5d20..4339a7dcbc 100644 --- a/templates/dashboard/instructor/analytics/stat-card.php +++ b/templates/dashboard/instructor/analytics/stat-card.php @@ -13,7 +13,6 @@ // Default values. $icon_size = $icon_size ?? 24; -$variation = isset( $variation ) ? $variation : 'enrolled'; $value = isset( $value ) ? $value : 0; $change = isset( $change ) ? $change : ''; $show_graph = isset( $show_graph ) ? $show_graph : false; @@ -30,7 +29,7 @@ $change_display = ! empty( $change ) ? $change : ''; ?> -
+
From 6f07de30f907403cc137be646ba4c0d79d78a3e3 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 26 Jan 2026 16:08:25 +0600 Subject: [PATCH 081/109] Conditional class toggling for tab elements. --- components/Tabs.php | 154 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 2 deletions(-) diff --git a/components/Tabs.php b/components/Tabs.php index 43ea1f9c69..5324bd6cf3 100644 --- a/components/Tabs.php +++ b/components/Tabs.php @@ -108,6 +108,54 @@ class Tabs extends BaseComponent { 'paramName' => 'tab', ); + /** + * CSS class for the main tabs wrapper element. + * + * @since 4.0.0 + * @var string + */ + protected string $wrapper_class = ''; + + /** + * CSS class for the tab list container. + * + * @since 4.0.0 + * @var string + */ + protected string $tab_list_class = ''; + + /** + * CSS class applied to each tab button element. + * + * @since 4.0.0 + * @var string + */ + protected string $tab_button_class = ''; + + /** + * CSS class for the inner content wrapper inside a tab button + * + * @since 4.0.0 + * @var string + */ + protected string $tab_button_content_class = ''; + + /** + * CSS class for the tab label text. + * + * @since 4.0.0 + * @var string + */ + protected string $tab_label_class = ''; + + /** + * CSS class for the sub-label text. + * + * @since 4.0.0 + * @var string + */ + protected string $tab_sub_label_class = ''; + /** * Set the tab data. * @@ -172,6 +220,101 @@ public function url_params( array $params ) { return $this; } + /** + * Set the CSS class for the main tabs wrapper element. + * + * @since 4.0.0 + * + * @param string $wrapper_class CSS class name(s) for the wrapper. + * @return $this + */ + public function wrapper_class( string $wrapper_class ) { + + $this->wrapper_class = ! empty( $wrapper_class ) + ? sprintf( ' class="%s"', esc_attr( $wrapper_class ) ) + : ''; + + return $this; + } + + + /** + * Set the CSS class for the tab list container. + * + * @since 4.0.0 + * + * @param string $tab_list_class CSS class name(s) for the tab list. + * @return $this + */ + public function tab_list_class( string $tab_list_class ) { + + $this->tab_list_class = esc_attr( $tab_list_class ); + + return $this; + } + + + /** + * Set the CSS class for each tab button element. + * + * @since 4.0.0 + * + * @param string $tab_button_class CSS class name(s) for the tab button. + * @return $this + */ + public function tab_button_class( string $tab_button_class ) { + $this->tab_button_class = ! empty( $tab_button_class ) + ? sprintf( ' class="%s"', esc_attr( $tab_button_class ) ) + : ''; + return $this; + } + + /** + * Set the CSS class for the inner content wrapper inside a tab button. + * + * @since 4.0.0 + * + * @param string $tab_button_content_class CSS class name(s) for the tab button content. + * @return $this + */ + public function tab_button_content_class( string $tab_button_content_class ) { + $this->tab_button_content_class = ! empty( $tab_button_content_class ) + ? sprintf( ' class="%s"', esc_attr( $tab_button_content_class ) ) + : ''; + return $this; + } + + /** + * Set the CSS class for the tab label text. + * + * @since 4.0.0 + * + * @param string $tab_label_class CSS class name(s) for the tab label. + * @return $this + */ + public function tab_label_class( string $tab_label_class ) { + $this->tab_label_class = ! empty( $tab_label_class ) + ? sprintf( ' class="%s"', esc_attr( $tab_label_class ) ) + : ''; + return $this; + } + + + /** + * Set the CSS class for the tab sub-label text. + * + * @since 4.0.0 + * + * @param string $tab_sub_label_class CSS class name(s) for the tab sub-label. + * @return $this + */ + public function tab_sub_label_class( string $tab_sub_label_class ) { + $this->tab_sub_label_class = ! empty( $tab_sub_label_class ) + ? sprintf( ' class="%s"', esc_attr( $tab_sub_label_class ) ) + : ''; + return $this; + } + /** * Get the tabs component. * @@ -194,8 +337,9 @@ public function get(): string { defaultTab: "", urlParams: })' + wrapper_class; // phpcs:ignore ?> > -
+
From ffc4dab1eba504f3adc7ccf2ef18154224195ca6 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 26 Jan 2026 17:58:32 +0600 Subject: [PATCH 082/109] export.svg added for Instructor Export --- assets/images/instructor-export.svg | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 assets/images/instructor-export.svg diff --git a/assets/images/instructor-export.svg b/assets/images/instructor-export.svg new file mode 100644 index 0000000000..41e3c4ded8 --- /dev/null +++ b/assets/images/instructor-export.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 2a1c8d230c8bb187660b4ca316be3a1bf86fab03 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Mon, 26 Jan 2026 18:03:47 +0600 Subject: [PATCH 083/109] Date Filter functionalities added --- components/DateFilter.php | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/components/DateFilter.php b/components/DateFilter.php index acc7522d1c..a611327229 100644 --- a/components/DateFilter.php +++ b/components/DateFilter.php @@ -10,9 +10,9 @@ namespace Tutor\Components; -use Tutor\Components\Constants\Size; use TUTOR\Icon; use TUTOR\Input; +use Tutor\Components\Constants\Size; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -91,7 +91,8 @@ class DateFilter extends BaseComponent { */ protected $icon_class; - /* Button size. + /** + * Button size. * * @var string */ @@ -104,6 +105,15 @@ class DateFilter extends BaseComponent { */ protected $icon_size = 20; + /** + * Whether to display the label text. + * + * @since 4.0.0 + * + * @var bool + */ + protected $show_label = true; + /** * Set filter type. * @@ -185,6 +195,19 @@ public function icon_class( string $icon_class ): self { return $this; } + /** + * Enable or disable the display of the label text. + * + * @since 4.0.0 + * + * @param bool $show_label True to show the label, false to hide it. + * @return $this + */ + public function show_label( bool $show_label ) { + $this->show_label = $show_label; + return $this; + } + /** * Render the component. * @@ -292,6 +315,10 @@ protected function calculate_label(): string { return Input::get( 'date', '' ); } + if ( ! $this->show_label ) { + return ''; + } + $start_date = Input::get( 'start_date' ); $end_date = Input::get( ( 'end_date' ) ); From 60e58297e301286cc5008afc2fd21f4b653d1538 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 27 Jan 2026 13:07:09 +0600 Subject: [PATCH 084/109] instructor home page icon issue fix --- assets/src/scss/frontend/dashboard/_stat-card.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/src/scss/frontend/dashboard/_stat-card.scss b/assets/src/scss/frontend/dashboard/_stat-card.scss index bd60f186de..cc5236b456 100644 --- a/assets/src/scss/frontend/dashboard/_stat-card.scss +++ b/assets/src/scss/frontend/dashboard/_stat-card.scss @@ -123,6 +123,7 @@ } } + &-star-line, &-star-fill, &-reviews { .tutor-stat-card-icon { From 97bdd7f3fd831bdee8214effdaa9fb106c3338d1 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Tue, 27 Jan 2026 18:06:21 +0600 Subject: [PATCH 085/109] Condition added for free, pro and report addon enable/disable --- classes/Utils.php | 36 ++- templates/dashboard/instructor/home.php | 240 ++++++++++-------- .../instructor/home/overview-chart.php | 2 + 3 files changed, 156 insertions(+), 122 deletions(-) diff --git a/classes/Utils.php b/classes/Utils.php index 447f83c6f4..5876014ece 100644 --- a/classes/Utils.php +++ b/classes/Utils.php @@ -4247,16 +4247,18 @@ public function get_reviews_by_user( $user_id = 0, $offset = 0, $limit = null, $ * @since 1.0.0 * @since 1.4.0 $course_id $date_filter param added. * @since 1.9.9 Course id & date filter is sorting with specific course and date. + * @since 4.0.0 $args parameter added. * * @param int $instructor_id user id. * @param int $offset offset. * @param int $limit limit. * @param string $course_id course id. * @param string $date_filter date filter. + * @param array $args Optional query arguments. * * @return array|null|object */ - public function get_reviews_by_instructor( $instructor_id = 0, $offset = 0, $limit = 150, $course_id = '', $date_filter = '' ) { + public function get_reviews_by_instructor( $instructor_id = 0, $offset = 0, $limit = 150, $course_id = '', $date_filter = '', $args = array() ) { global $wpdb; $instructor_id = sanitize_text_field( $instructor_id ); $offset = sanitize_text_field( $offset ); @@ -4264,10 +4266,17 @@ public function get_reviews_by_instructor( $instructor_id = 0, $offset = 0, $lim $course_id = sanitize_text_field( $course_id ); $date_filter = sanitize_text_field( $date_filter ); $instructor_id = $this->get_user_id( $instructor_id ); + $args = $this->sanitize_array( $args ); $course_query = ''; $date_query = ''; + $where_clause = ''; + + if ( ! empty( $args['where'] ) ) { + $where_clause = ' AND ' . $args['where']; + } + if ( '' !== $course_id ) { $course_query = " AND {$wpdb->comments}.comment_post_ID = {$course_id} "; } @@ -4298,6 +4307,7 @@ public function get_reviews_by_instructor( $instructor_id = 0, $offset = 0, $lim WHERE {$wpdb->comments}.comment_post_ID IN({$implode_ids}) AND comment_type = %s AND meta_key = %s + {$where_clause} {$course_query} {$date_query} ", @@ -4306,6 +4316,8 @@ public function get_reviews_by_instructor( $instructor_id = 0, $offset = 0, $lim ) ); + $order_by = $args['order_by'] ?? 'comment_ID'; + // Results. $results['results'] = $wpdb->get_results( $wpdb->prepare( @@ -4320,21 +4332,21 @@ public function get_reviews_by_instructor( $instructor_id = 0, $offset = 0, $lim {$wpdb->users}.display_name, {$wpdb->posts}.post_title as course_title - FROM {$wpdb->comments} - INNER JOIN {$wpdb->commentmeta} - ON {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id - INNER JOIN {$wpdb->users} - ON {$wpdb->comments}.user_id = {$wpdb->users}.ID - INNER JOIN {$wpdb->posts} - ON {$wpdb->posts}.ID = {$wpdb->comments}.comment_post_ID - WHERE {$wpdb->comments}.comment_post_ID IN({$implode_ids}) + FROM {$wpdb->comments} + INNER JOIN {$wpdb->commentmeta} + ON {$wpdb->comments}.comment_ID = {$wpdb->commentmeta}.comment_id + INNER JOIN {$wpdb->users} + ON {$wpdb->comments}.user_id = {$wpdb->users}.ID + INNER JOIN {$wpdb->posts} + ON {$wpdb->posts}.ID = {$wpdb->comments}.comment_post_ID + WHERE {$wpdb->comments}.comment_post_ID IN({$implode_ids}) AND comment_type = %s AND meta_key = %s + {$where_clause} {$course_query} {$date_query} - ORDER BY comment_ID DESC - LIMIT %d, %d; - ", + ORDER BY {$order_by} DESC + LIMIT %d, %d", 'tutor_course_rating', 'tutor_rating', $offset, diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 82bf979a31..a5c04351c0 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -16,11 +16,11 @@ use TUTOR_REPORT\Analytics; use Tutor\Models\CourseModel; use Tutor\Helpers\QueryHelper; +use Tutor\Models\WithdrawModel; use Tutor\Components\DateFilter; use Tutor\Components\InputField; use Tutor\Components\Constants\InputType; - $sortable_sections = array( array( 'id' => 'current_stats', @@ -80,6 +80,7 @@ function ( $carry, $section ) { $upcoming_tasks = array(); $get_upcoming_live_tasks = array(); +$overview_chart_data = array(); $user = wp_get_current_user(); $instructor_course_ids = CourseModel::get_courses_by_args( @@ -90,35 +91,45 @@ function ( $carry, $section ) { ) )->posts; +$tutor_pro_enabled = tutor_utils()->is_plugin_active( 'tutor-pro/tutor-pro.php' ); +$report_addon_enabled = tutor_utils()->is_addon_enabled( 'tutor-report' ); -$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; -$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; -$previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); +$start_date = Input::has( 'start_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'start_date' ) ) : ''; +$end_date = Input::has( 'end_date' ) ? tutor_get_formated_date( 'Y-m-d', Input::get( 'end_date' ) ) : ''; +// @todo Implementation is on hold until the new designs are ready." +// $previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); // Total Earnings. -$total_earnings = Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date ); -$previous_period_earnings = Analytics::get_earnings_by_user( $user->ID, '', $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )['total_earnings'] ?? 0; +$total_earnings = $tutor_pro_enabled && $report_addon_enabled + ? ( Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date )['total_earnings'] ?? 0 ) + : ( WithdrawModel::get_withdraw_summary( $user->ID )->total_income ?? 0 ); + +// @todo Implementation is on hold until the new designs are ready. +// $previous_period_earnings = Analytics::get_earnings_by_user( $user->ID, '', $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )['total_earnings'] ?? 0; // Total Courses. -$total_courses = CourseModel::get_course_count_by_date( $start_date, $end_date, $user->ID ); -$previous_period_courses = CourseModel::get_course_count_by_date( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); +$total_courses = CourseModel::get_course_count_by_date( $start_date, $end_date, $user->ID ); +// @todo Implementation is on hold until the new designs are ready. +// $previous_period_courses = CourseModel::get_course_count_by_date( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); // Total Students. -$total_students = Instructor::get_instructor_total_students_by_date_range( $start_date, $end_date, $user->ID ); -$previous_period_students = Instructor::get_instructor_total_students_by_date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); +$total_students = Instructor::get_instructor_total_students_by_date_range( $start_date, $end_date, $user->ID ); +// @todo Implementation is on hold until the new designs are ready. +// $previous_period_students = Instructor::get_instructor_total_students_by_date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); // Total Ratings. -$where = empty( $start_date ) && empty( $end_date ) ? array() : array( 'reviews.comment_date' => array( 'BETWEEN', array( $start_date, $end_date ) ) ); -$total_ratings = tutor_utils()->get_instructor_ratings( $user->ID, $where ); -$previous_period_ratings = tutor_utils()->get_instructor_ratings( $user->ID, array( 'reviews.comment_date' => array( 'BETWEEN', array( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] ) ) ) ); +$where = empty( $start_date ) && empty( $end_date ) ? array() : array( 'reviews.comment_date' => array( 'BETWEEN', array( $start_date, $end_date ) ) ); +$total_ratings = tutor_utils()->get_instructor_ratings( $user->ID, $where ); +// @todo Implementation is on hold until the new designs are ready. +// $previous_period_ratings = tutor_utils()->get_instructor_ratings( $user->ID, array( 'reviews.comment_date' => array( 'BETWEEN', array( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] ) ) ) ); $stat_cards = array( array( 'variation' => 'success', 'title' => esc_html__( 'Total Earnings', 'tutor' ), 'icon' => Icon::EARNING, - 'value' => wp_kses_post( tutor_utils()->tutor_price( $total_earnings['total_earnings'] ?? 0 ) ), - 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_earnings['total_earnings'] ?? 0, $previous_period_earnings ), + 'value' => wp_kses_post( tutor_utils()->tutor_price( $total_earnings ?? 0 ) ), + // 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_earnings['total_earnings'] ?? 0, $previous_period_earnings ), // 'data' => array( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ), @todo will be added later. ), array( @@ -126,7 +137,7 @@ function ( $carry, $section ) { 'title' => esc_html__( 'Total Courses', 'tutor' ), 'icon' => Icon::COURSES, 'value' => $total_courses, - 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_courses, $previous_period_courses, false ), + // 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_courses, $previous_period_courses, false ), // 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), @todo will be added later. ), array( @@ -134,7 +145,7 @@ function ( $carry, $section ) { 'title' => esc_html__( 'Total Students', 'tutor' ), 'icon' => Icon::PASSED, 'value' => $total_students, - 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_students, $previous_period_students, false ), + // 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_students, $previous_period_students, false ), // 'data' => array( 0, 8, 5, 2, 3, 4, 5, 6, 7, 8, 9 ), ), array( @@ -142,21 +153,23 @@ function ( $carry, $section ) { 'title' => esc_html__( 'Avg. Rating', 'tutor' ), 'icon' => Icon::STAR_LINE, 'value' => $total_ratings->rating_avg, - 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_ratings->rating_avg, $previous_period_ratings->rating_avg, false ), + // 'change' => Instructor::get_stat_card_comparison_subtitle( $start_date, $end_date, $total_ratings->rating_avg, $previous_period_ratings->rating_avg, false ), // 'data' => array( 4.5, 4.2, 3, 3, 2.8, 2, 4.5, 4.2, 3, 2, 1, 0 ), ), ); // Graph. -$labels = wp_list_pluck( $total_earnings['earnings'], 'label_name' ); -$graph_earnings = array_map( 'intval', wp_list_pluck( $total_earnings['earnings'], 'total' ) ); -$enrollments = Analytics::get_total_students_by_user( $user->ID, '', $start_date, $end_date ); -$graph_enrollments = array_map( 'intval', wp_list_pluck( $enrollments['enrollments'], 'total' ) ); -$overview_chart_data = array( - 'earnings' => array_merge( array( 0 ), $graph_earnings, array( 0 ) ), - 'enrolled' => array_merge( array( 0 ), $graph_enrollments, array( 0 ) ), - 'labels' => array_merge( array( '' ), $labels, array( '' ) ), -); +if ( $tutor_pro_enabled && $report_addon_enabled ) { + $labels = wp_list_pluck( $total_earnings['earnings'], 'label_name' ); + $graph_earnings = array_map( 'intval', wp_list_pluck( $total_earnings['earnings'], 'total' ) ); + $enrollments = Analytics::get_total_students_by_user( $user->ID, '', $start_date, $end_date ); + $graph_enrollments = array_map( 'intval', wp_list_pluck( $enrollments['enrollments'], 'total' ) ); + $overview_chart_data = array( + 'earnings' => array_merge( array( 0 ), $graph_earnings, array( 0 ) ), + 'enrolled' => array_merge( array( 0 ), $graph_enrollments, array( 0 ) ), + 'labels' => array_merge( array( '' ), $labels, array( '' ) ), + ); +} // Course Completion Distribution. $course_completion_distribution = Instructor::get_course_completion_distribution_data_by_instructor( $instructor_course_ids ); @@ -186,24 +199,24 @@ function ( $carry, $section ) { // @todo Will be added on later. // $leaderboard_data = array( -// array( -// 'name' => esc_html__( 'John Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', -// 'no_of_courses' => 10, -// 'completion_percentage' => 50, -// ), -// array( -// 'name' => esc_html__( 'Jane Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a042581f4e29026704d', -// 'no_of_courses' => 20, -// 'completion_percentage' => 30, -// ), -// array( -// 'name' => esc_html__( 'Bob Doe', 'tutor' ), -// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826732d', -// 'no_of_courses' => 30, -// 'completion_percentage' => 70, -// ), +// array( +// 'name' => esc_html__( 'John Doe', 'tutor' ), +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826712d', +// 'no_of_courses' => 10, +// 'completion_percentage' => 50, +// ), +// array( +// 'name' => esc_html__( 'Jane Doe', 'tutor' ), +// 'avatar' => 'https://i.pravatar.cc/300?u=a042581f4e29026704d', +// 'no_of_courses' => 20, +// 'completion_percentage' => 30, +// ), +// array( +// 'name' => esc_html__( 'Bob Doe', 'tutor' ), +// 'avatar' => 'https://i.pravatar.cc/300?u=a04258a2462d826732d', +// 'no_of_courses' => 30, +// 'completion_percentage' => 70, +// ), // ); // Top Performing Courses. @@ -225,46 +238,48 @@ function ( $course ) { $top_courses ); -if ( empty( $start_date ) && empty( $end_date ) ) { - $get_upcoming_live_tasks = get_posts( +if ( empty( $start_date ) && empty( $end_date ) && $tutor_pro_enabled ) { + + $meta_keys = array_filter( array( - 'post_type' => array( tutor()->zoom_post_type, tutor()->meet_post_type ), - 'post_status' => 'publish', - 'post_author' => $user->ID, - 'numberposts' => 5, - 'meta_query' => array( - array( - 'key' => array( '_tutor_zm_start_datetime', 'tutor-google-meet-start-datetime' ), - 'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ), - 'compare' => '>=', - 'type' => 'DATETIME', - ), - ), + tutor_utils()->is_addon_enabled( 'google-meet' ) ? 'tutor-google-meet-start-datetime' : null, + tutor_utils()->is_addon_enabled( 'tutor-zoom' ) ? '_tutor_zm_start_datetime' : null, ) ); + if ( ! empty( $meta_key ) ) { + $get_upcoming_live_tasks = get_posts( + array( + 'post_type' => array( tutor()->zoom_post_type, tutor()->meet_post_type ), + 'post_status' => 'publish', + 'post_author' => $user->ID, + 'numberposts' => 5, + 'meta_query' => array( + array( + 'key' => array( '_tutor_zm_start_datetime', 'tutor-google-meet-start-datetime' ), + 'value' => gmdate( 'Y-m-d H:i:s', strtotime( 'now' ) ), + 'compare' => '>=', + 'type' => 'DATETIME', + ), + ), + ) + ); + } + if ( ! empty( $get_upcoming_live_tasks ) ) { $upcoming_tasks = array_map( function ( $task ) { - switch ( $task->post_type ) { - case tutor()->zoom_post_type: - $meta_key = '_tutor_zm_start_datetime'; - $url = json_decode( get_post_meta( $task->ID, '_tutor_zm_data' )[0] )->join_url ?? ''; - break; + $is_zoom = tutor()->zoom_post_type === $task->post_type; + $is_meet = tutor()->meet_post_type === $task->post_type; - case tutor()->meet_post_type: - $meta_key = 'tutor-google-meet-start-datetime'; - $url = get_post_meta( $task->ID, 'tutor-google-meet-link', true ); - break; + $live_meta_key = $is_zoom ? '_tutor_zm_start_datetime' + : ( $is_meet ? 'tutor-google-meet-start-datetime' : '' ); - default: - $meta_key = null; - $url = ''; - break; - } + $url = $is_zoom ? ( json_decode( get_post_meta( $task->ID, '_tutor_zm_data' )[0] )->join_url ?? '' ) + : ( $is_meet ? get_post_meta( $task->ID, 'tutor-google-meet-link', true ) : '' ); - $start_date = get_post_meta( $task->ID, $meta_key, true ); + $start_date = get_post_meta( $task->ID, $live_meta_key, true ); return array( 'name' => $task->post_title, @@ -317,23 +332,26 @@ function ( $task ) { if ( ! empty( $start_date ) && ! empty( $end_date ) ) { $review_where['comment_date'] = array( 'BETWEEN', array( $start_date, $end_date ) ); } -$review_args = array( 'where' => QueryHelper::prepare_where_clause( $review_where ) ); -$reviews = Analytics::get_reviews( 3, $review_args ); -$recent_reviews = array_map( - function ( $review ) { - return array( - 'user' => array( - 'name' => $review->display_name, - 'avatar' => get_avatar_url( $review->user_id ), - ), - 'course_name' => get_the_title( $review->comment_post_ID ), - 'date' => $review->comment_date, - 'rating' => $review->rating, - 'review_text' => $review->comment_content, - ); - }, - $reviews -); +$review_args = array( 'where' => QueryHelper::prepare_where_clause( $review_where ) ); +$reviews = tutor_utils()->get_reviews_by_instructor( $user->ID, 0, 3, '', '', $review_args ); + +if ( ! empty( $reviews->results ) ) { + $recent_reviews = array_map( + function ( $review ) { + return array( + 'user' => array( + 'name' => $review->display_name, + 'avatar' => get_avatar_url( $review->user_id ), + ), + 'course_name' => get_the_title( $review->comment_post_ID ), + 'date' => $review->comment_date, + 'rating' => $review->rating, + 'review_text' => $review->comment_content, + ); + }, + $reviews->results + ); +} ?> render(); ?> @@ -450,7 +470,7 @@ class="tutor-flex tutor-gap-6" -
-
- -
+
+
+ +
-
- - $review ), - ); - ?> - +
+ + $review ), + ); + ?> + +
-
diff --git a/templates/dashboard/instructor/home/overview-chart.php b/templates/dashboard/instructor/home/overview-chart.php index 726abc6ce3..7a6646619c 100644 --- a/templates/dashboard/instructor/home/overview-chart.php +++ b/templates/dashboard/instructor/home/overview-chart.php @@ -12,6 +12,7 @@ ?> +
+ From 6899c6f226c91033df45f34035c96000e39697d8 Mon Sep 17 00:00:00 2001 From: Sanjana Khan Date: Wed, 28 Jan 2026 04:20:00 +0600 Subject: [PATCH 086/109] error message in instructor home page --- templates/dashboard/instructor/home.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index a5c04351c0..01bcdfe142 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -160,8 +160,8 @@ function ( $carry, $section ) { // Graph. if ( $tutor_pro_enabled && $report_addon_enabled ) { - $labels = wp_list_pluck( $total_earnings['earnings'], 'label_name' ); - $graph_earnings = array_map( 'intval', wp_list_pluck( $total_earnings['earnings'], 'total' ) ); + $labels = wp_list_pluck( $total_earnings, 'label_name' ); + $graph_earnings = array_map( 'intval', wp_list_pluck( $total_earnings, 'total' ) ); $enrollments = Analytics::get_total_students_by_user( $user->ID, '', $start_date, $end_date ); $graph_enrollments = array_map( 'intval', wp_list_pluck( $enrollments['enrollments'], 'total' ) ); $overview_chart_data = array( From bc0f5af1825cb5a36c4608ee71702eee9c670919 Mon Sep 17 00:00:00 2001 From: sanjana4khan Date: Wed, 28 Jan 2026 11:12:29 +0600 Subject: [PATCH 087/109] condition added for free, pro and report addon in instructor home page. --- models/OrderModel.php | 3 ++- templates/dashboard/instructor/home.php | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/models/OrderModel.php b/models/OrderModel.php index e7f2efde29..2fcc11fd86 100644 --- a/models/OrderModel.php +++ b/models/OrderModel.php @@ -1338,7 +1338,8 @@ public function get_refunds_by_user( int $user_id, string $period = '', $start_d $wpdb->prepare( "SELECT COALESCE(SUM(o.refund_amount), 0) AS total, - created_at_gmt AS date_format + created_at_gmt AS date_format, + DATE_FORMAT(o.created_at_gmt, '%b') AS label_name FROM {$this->table_name} AS o -- LEFT JOIN {$item_table} AS i ON i.order_id = o.id -- LEFT JOIN {$wpdb->posts} AS c ON c.id = i.item_id diff --git a/templates/dashboard/instructor/home.php b/templates/dashboard/instructor/home.php index 01bcdfe142..a726c3b6fa 100644 --- a/templates/dashboard/instructor/home.php +++ b/templates/dashboard/instructor/home.php @@ -100,9 +100,12 @@ function ( $carry, $section ) { // $previous_dates = Instructor::get_comparison_date_range( $start_date, $end_date ); // Total Earnings. -$total_earnings = $tutor_pro_enabled && $report_addon_enabled - ? ( Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date )['total_earnings'] ?? 0 ) - : ( WithdrawModel::get_withdraw_summary( $user->ID )->total_income ?? 0 ); +if ( $tutor_pro_enabled && $report_addon_enabled ) { + $earnings = Analytics::get_earnings_by_user( $user->ID, '', $start_date, $end_date ); + $total_earnings = $earnings['total_earnings'] ?? 0; +} else { + $total_earnings = WithdrawModel::get_withdraw_summary( $user->ID )->total_income ?? 0; +} // @todo Implementation is on hold until the new designs are ready. // $previous_period_earnings = Analytics::get_earnings_by_user( $user->ID, '', $previous_dates['previous_start_date'], $previous_dates['previous_end_date'] )['total_earnings'] ?? 0; @@ -113,7 +116,8 @@ function ( $carry, $section ) { // $previous_period_courses = CourseModel::get_course_count_by_date( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); // Total Students. -$total_students = Instructor::get_instructor_total_students_by_date_range( $start_date, $end_date, $user->ID ); +$total_students_where = empty( $start_date ) && empty( $end_date ) ? array() : array( 'enrollment.post_date' => array( 'BETWEEN', array( $start_date, $end_date ) ) ); +$total_students = Instructor::get_instructor_total_students_by_date_range( $start_date, $end_date, $user->ID ); // @todo Implementation is on hold until the new designs are ready. // $previous_period_students = Instructor::get_instructor_total_students_by_date_range( $previous_dates['previous_start_date'], $previous_dates['previous_end_date'], $user->ID ); @@ -160,8 +164,8 @@ function ( $carry, $section ) { // Graph. if ( $tutor_pro_enabled && $report_addon_enabled ) { - $labels = wp_list_pluck( $total_earnings, 'label_name' ); - $graph_earnings = array_map( 'intval', wp_list_pluck( $total_earnings, 'total' ) ); + $labels = wp_list_pluck( $earnings['earnings'], 'label_name' ); + $graph_earnings = array_map( 'intval', wp_list_pluck( $earnings['earnings'], 'total' ) ); $enrollments = Analytics::get_total_students_by_user( $user->ID, '', $start_date, $end_date ); $graph_enrollments = array_map( 'intval', wp_list_pluck( $enrollments['enrollments'], 'total' ) ); $overview_chart_data = array( @@ -364,11 +368,10 @@ class="tutor-flex tutor-flex-column tutor-gap-6 tutor-mt-7" >
- - - - type( DateFilter::TYPE_RANGE )->placement( 'bottom-start' )->render(); ?> - + + type( DateFilter::TYPE_RANGE )->placement( 'bottom-start' )->render(); ?> + +