diff --git a/.gitignore b/.gitignore index 0748d1e1d3e..6859e686c65 100644 --- a/.gitignore +++ b/.gitignore @@ -37,10 +37,6 @@ /coverage/* /client/coverage/* -# Ignore additional themes -/app/themes/* -!/app/themes/default/* - # Ignore public download/upload folders /public/downloads/* /public/uploads/* diff --git a/app/assets/stylesheets/breadcrumbs.scss b/app/assets/stylesheets/breadcrumbs.scss deleted file mode 100644 index dfce8b2511c..00000000000 --- a/app/assets/stylesheets/breadcrumbs.scss +++ /dev/null @@ -1,3 +0,0 @@ -.breadcrumb > li { - display: inline; -} diff --git a/app/assets/stylesheets/course/dangerously-fit-height-to-viewport.scss b/app/assets/stylesheets/course/dangerously-fit-height-to-viewport.scss deleted file mode 100644 index 389c6434aba..00000000000 --- a/app/assets/stylesheets/course/dangerously-fit-height-to-viewport.scss +++ /dev/null @@ -1,5 +0,0 @@ -// TODO: Remove once sidebar is fixed to fill viewport until bottom of viewport. -// 126px is the height of the topbar + breadcrumb + margins in between. -.dangerously-fit-height-to-viewport { - height: calc(100vh - 126px); -} diff --git a/app/controllers/announcements_controller.rb b/app/controllers/announcements_controller.rb index 1e35cbd2dd6..0eec13d17e7 100644 --- a/app/controllers/announcements_controller.rb +++ b/app/controllers/announcements_controller.rb @@ -1,15 +1,13 @@ # frozen_string_literal: true class AnnouncementsController < ApplicationController - load_resource :announcement, class: GenericAnnouncement.name, only: :mark_as_read, - id_param: :announcement_id - - add_breadcrumb :index, :announcements_path + load_resource :announcement, class: GenericAnnouncement.name, only: :mark_as_read, id_param: :announcement_id def index respond_to do |format| format.html format.json do - @announcements = global_announcements.includes(:creator) + announcements = requesting_unread? ? unread_global_announcements : global_announcements + @announcements = announcements.includes(:creator) end end end @@ -20,4 +18,16 @@ def mark_as_read @announcement.mark_as_read! for: current_user head :ok end + + protected + + def publicly_accessible? + requesting_unread? + end + + private + + def requesting_unread? + params[:unread] == 'true' + end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index bbadb13c448..6761178fee9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,9 +7,6 @@ class ApplicationController < ActionController::Base # Custom flash types. We follow Bootstrap's convention. add_flash_types :success, :info, :warning, :danger - # We allow views to add breadcrumbs - helper_method :add_breadcrumb - include ApplicationControllerMultitenancyConcern include ApplicationComponentsConcern include ApplicationInternationalizationConcern diff --git a/app/controllers/components/course/achievements_component.rb b/app/controllers/components/course/achievements_component.rb index 17fcc73b9c9..25644990f9d 100644 --- a/app/controllers/components/course/achievements_component.rb +++ b/app/controllers/components/course/achievements_component.rb @@ -14,7 +14,7 @@ def sidebar_items [ { key: :achievements, - icon: 'trophy', + icon: :achievement, title: I18n.t('course.achievement.achievements.sidebar_title'), weight: 4, path: course_achievements_path(current_course), diff --git a/app/controllers/components/course/announcements_component.rb b/app/controllers/components/course/announcements_component.rb index b4e0ec81ef7..a82836bc70a 100644 --- a/app/controllers/components/course/announcements_component.rb +++ b/app/controllers/components/course/announcements_component.rb @@ -16,7 +16,7 @@ def main_sidebar_items [ { key: :announcements, - icon: 'bullhorn', + icon: :announcement, title: settings.title || t('course.announcements.sidebar_title'), weight: 1, path: course_announcements_path(current_course), diff --git a/app/controllers/components/course/assessments_component.rb b/app/controllers/components/course/assessments_component.rb index 71521af429f..6b242b3bcca 100644 --- a/app/controllers/components/course/assessments_component.rb +++ b/app/controllers/components/course/assessments_component.rb @@ -25,10 +25,10 @@ def assessment_categories current_course.assessment_categories.select(&:persisted?).map do |category| { key: "assessments_#{category.id}", - icon: 'plane', # TODO: category.icon in db that user can select and set + icon: :assessment, # TODO: category.icon in db that user can select and set title: category.title, weight: 2, - path: course_assessments_path(current_course, category: category, tab: category.tabs.first), + path: course_assessments_path(current_course, category: category), unread: 0 } end @@ -38,7 +38,7 @@ def assessment_submissions [ { key: :assessments_submissions, - icon: 'upload', + icon: :submission, title: t('course.assessment.submissions.sidebar_title'), weight: 3, path: course_submissions_path(current_course), @@ -53,7 +53,7 @@ def admin_sidebar_items [ { key: :assessments_skills, - icon: 'bolt', + icon: :skills, title: t('course.assessment.skills.sidebar_title'), type: :admin, weight: 8, diff --git a/app/controllers/components/course/discussion/topics_component.rb b/app/controllers/components/course/discussion/topics_component.rb index f083e76fffe..5fdadc72759 100644 --- a/app/controllers/components/course/discussion/topics_component.rb +++ b/app/controllers/components/course/discussion/topics_component.rb @@ -17,7 +17,7 @@ def main_sidebar_items [ { key: :discussion_topics, - icon: 'comments', + icon: :comments, title: settings.title || t('course.discussion.topics.sidebar_title'), weight: 5, path: course_topics_path(current_course), diff --git a/app/controllers/components/course/duplication_component.rb b/app/controllers/components/course/duplication_component.rb index 8300b1ac571..c9d1c7b3c81 100644 --- a/app/controllers/components/course/duplication_component.rb +++ b/app/controllers/components/course/duplication_component.rb @@ -12,7 +12,7 @@ def sidebar_items [ { key: :duplication, - icon: 'clone', + icon: :duplication, title: t('layouts.duplication.title'), type: :admin, weight: 5, diff --git a/app/controllers/components/course/experience_points_component.rb b/app/controllers/components/course/experience_points_component.rb index c37e9a55671..9f110192800 100644 --- a/app/controllers/components/course/experience_points_component.rb +++ b/app/controllers/components/course/experience_points_component.rb @@ -15,7 +15,8 @@ def sidebar_items [ { - icon: 'magic', + key: :disburse_experience_points, + icon: :disbursement, title: t('course.experience_points.disbursement.sidebar_title'), type: :admin, weight: 4, diff --git a/app/controllers/components/course/forums_component.rb b/app/controllers/components/course/forums_component.rb index 46195959001..93684637214 100644 --- a/app/controllers/components/course/forums_component.rb +++ b/app/controllers/components/course/forums_component.rb @@ -16,7 +16,7 @@ def main_sidebar_items [ { key: :forums, - icon: 'list-ul', + icon: :forum, title: settings.title || t('course.forum.forums.sidebar_title'), weight: 10, path: course_forums_path(current_course), diff --git a/app/controllers/components/course/groups_component.rb b/app/controllers/components/course/groups_component.rb index dd51ddb2075..ec5c42b0360 100644 --- a/app/controllers/components/course/groups_component.rb +++ b/app/controllers/components/course/groups_component.rb @@ -13,7 +13,7 @@ def sidebar_items [ { key: :groups, - icon: 'share-alt', + icon: :groups, title: I18n.t('course.groups.sidebar_title'), type: :admin, weight: 7, diff --git a/app/controllers/components/course/leaderboard_component.rb b/app/controllers/components/course/leaderboard_component.rb index 40100cfdf66..ba618f1ee06 100644 --- a/app/controllers/components/course/leaderboard_component.rb +++ b/app/controllers/components/course/leaderboard_component.rb @@ -20,7 +20,7 @@ def main_sidebar_items [ { key: :leaderboard, - icon: 'star', + icon: :leaderboard, title: settings.title || t('course.leaderboards.sidebar_title'), weight: 6, path: course_leaderboard_path(current_course) diff --git a/app/controllers/components/course/learning_map_component.rb b/app/controllers/components/course/learning_map_component.rb index 046d6ed1004..d22e7661645 100644 --- a/app/controllers/components/course/learning_map_component.rb +++ b/app/controllers/components/course/learning_map_component.rb @@ -14,7 +14,7 @@ def sidebar_items [ { key: :learning_map, - icon: 'map', + icon: :map, title: t('layouts.learning_map.title'), weight: 5, path: course_learning_map_path(current_course) diff --git a/app/controllers/components/course/lesson_plan_component.rb b/app/controllers/components/course/lesson_plan_component.rb index 67b52be0694..043e44e5979 100644 --- a/app/controllers/components/course/lesson_plan_component.rb +++ b/app/controllers/components/course/lesson_plan_component.rb @@ -20,7 +20,7 @@ def main_sidebar_items [ { key: :lesson_plan, - icon: 'book', + icon: :lessonPlan, title: I18n.t('course.lesson_plan.items.sidebar_title'), weight: 8, path: course_lesson_plan_path(current_course) diff --git a/app/controllers/components/course/levels_component.rb b/app/controllers/components/course/levels_component.rb index 631df2351b3..d2d75d04100 100644 --- a/app/controllers/components/course/levels_component.rb +++ b/app/controllers/components/course/levels_component.rb @@ -16,7 +16,7 @@ def sidebar_items [ { key: :levels, - icon: 'star-half-o', + icon: :levels, title: I18n.t('course.levels.sidebar_title'), type: :admin, weight: 6, diff --git a/app/controllers/components/course/materials_component.rb b/app/controllers/components/course/materials_component.rb index 5eed3565d97..5a0d05fbc84 100644 --- a/app/controllers/components/course/materials_component.rb +++ b/app/controllers/components/course/materials_component.rb @@ -16,7 +16,7 @@ def main_sidebar_items [ { key: :materials, - icon: 'folder-open', + icon: :material, title: settings.title || t('course.material.sidebar_title'), weight: 9, path: course_material_folder_path(current_course, current_course.root_folder), diff --git a/app/controllers/components/course/multiple_reference_timelines_component.rb b/app/controllers/components/course/multiple_reference_timelines_component.rb index 30ebc1bdd4b..7f76a32b3bb 100644 --- a/app/controllers/components/course/multiple_reference_timelines_component.rb +++ b/app/controllers/components/course/multiple_reference_timelines_component.rb @@ -16,7 +16,7 @@ def sidebar_items [ { key: :reference_timelines, - icon: 'random', + icon: :timelines, type: :admin, weight: 8, title: t('layouts.multiple_reference_timelines.timeline_designer'), diff --git a/app/controllers/components/course/settings_component.rb b/app/controllers/components/course/settings_component.rb index 49b96a0d8bc..8ce746b4758 100644 --- a/app/controllers/components/course/settings_component.rb +++ b/app/controllers/components/course/settings_component.rb @@ -22,7 +22,8 @@ def admin_sidebar_items [ { - icon: 'gear', + key: :admin, + icon: :settings, title: t('layouts.course_admin.title'), type: :admin, weight: 9, diff --git a/app/controllers/components/course/statistics_component.rb b/app/controllers/components/course/statistics_component.rb index bf36448a19b..101d0a01541 100644 --- a/app/controllers/components/course/statistics_component.rb +++ b/app/controllers/components/course/statistics_component.rb @@ -12,7 +12,7 @@ def sidebar_items [ { key: :statistics, - icon: 'bar-chart', + icon: :statistics, title: t('course.statistics.header'), type: :admin, weight: 2, diff --git a/app/controllers/components/course/survey_component.rb b/app/controllers/components/course/survey_component.rb index 082ef50a78c..1adcf2e71c0 100644 --- a/app/controllers/components/course/survey_component.rb +++ b/app/controllers/components/course/survey_component.rb @@ -14,7 +14,7 @@ def sidebar_items [ { key: :surveys, - icon: 'pie-chart', + icon: :survey, title: I18n.t('course.surveys.sidebar_title'), weight: 11, path: course_surveys_path(current_course) diff --git a/app/controllers/components/course/users_component.rb b/app/controllers/components/course/users_component.rb index efdeb3b2a65..c383ffaa22b 100644 --- a/app/controllers/components/course/users_component.rb +++ b/app/controllers/components/course/users_component.rb @@ -20,7 +20,7 @@ def main_sidebar_items [ { key: :users, - icon: 'group', + icon: :users, title: t('course.users.sidebar_title'), weight: 7, path: course_users_path(current_course) @@ -39,7 +39,8 @@ def admin_sidebar_items [ { - icon: 'user-plus', + key: :manage_users, + icon: :manageUsers, title: t('layouts.course_users.title'), type: :admin, weight: 1, diff --git a/app/controllers/components/course/videos_component.rb b/app/controllers/components/course/videos_component.rb index a97adfcb642..1abe751474a 100644 --- a/app/controllers/components/course/videos_component.rb +++ b/app/controllers/components/course/videos_component.rb @@ -24,7 +24,7 @@ def main_sidebar_items [ { key: :videos, - icon: 'video-camera', + icon: :video, title: settings.title || t('course.video.videos.sidebar_title'), weight: 12, path: course_videos_path(current_course, tab: current_course.default_video_tab), diff --git a/app/controllers/concerns/course/users_breadcrumb_concern.rb b/app/controllers/concerns/course/users_breadcrumb_concern.rb deleted file mode 100644 index 71977fea884..00000000000 --- a/app/controllers/concerns/course/users_breadcrumb_concern.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -module Course::UsersBreadcrumbConcern - extend ActiveSupport::Concern - - included do - add_breadcrumb I18n.t('breadcrumbs.course.users.index'), :course_users_path - end -end diff --git a/app/controllers/course/achievement/controller.rb b/app/controllers/course/achievement/controller.rb index b1c0b8ce138..69610655d8c 100644 --- a/app/controllers/course/achievement/controller.rb +++ b/app/controllers/course/achievement/controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::Achievement::Controller < Course::ComponentController load_and_authorize_resource :achievement, through: :course, class: Course::Achievement.name - add_breadcrumb :index, :course_achievements_path helper name diff --git a/app/controllers/course/admin/announcement_settings_controller.rb b/app/controllers/course/admin/announcement_settings_controller.rb index 4c4ac15bc0a..1840514ad1e 100644 --- a/app/controllers/course/admin/announcement_settings_controller.rb +++ b/app/controllers/course/admin/announcement_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::AnnouncementSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_announcements_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/assessment_settings_controller.rb b/app/controllers/course/admin/assessment_settings_controller.rb index a1f39b88b5a..4faa659de46 100644 --- a/app/controllers/course/admin/assessment_settings_controller.rb +++ b/app/controllers/course/admin/assessment_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::AssessmentSettingsController < Course::Admin::Controller - add_breadcrumb :index, :course_admin_assessments_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/assessments/categories_controller.rb b/app/controllers/course/admin/assessments/categories_controller.rb index 85f739f4871..61b11de322e 100644 --- a/app/controllers/course/admin/assessments/categories_controller.rb +++ b/app/controllers/course/admin/assessments/categories_controller.rb @@ -4,7 +4,6 @@ class Course::Admin::Assessments::CategoriesController < Course::Admin::Controll through: :course, through_association: :assessment_categories, class: Course::Assessment::Category.name - add_breadcrumb :index, :course_admin_assessments_path def new end diff --git a/app/controllers/course/admin/assessments/tabs_controller.rb b/app/controllers/course/admin/assessments/tabs_controller.rb index 23c5e689a88..5cdd5c2ca7f 100644 --- a/app/controllers/course/admin/assessments/tabs_controller.rb +++ b/app/controllers/course/admin/assessments/tabs_controller.rb @@ -8,9 +8,6 @@ class Course::Admin::Assessments::TabsController < Course::Admin::Controller through: :category, class: Course::Assessment::Tab.name - add_breadcrumb :index, :course_admin_assessments_path - before_action :add_category_breadcrumb - def new end @@ -38,10 +35,6 @@ def tab_params params.require(:tab).permit(:title, :weight) end - def add_category_breadcrumb - add_breadcrumb @category.title, course_admin_assessments_path(current_course) - end - # @return [Course::AssessmentsComponent] # @return [nil] If component is disabled. def component diff --git a/app/controllers/course/admin/codaveri_settings_controller.rb b/app/controllers/course/admin/codaveri_settings_controller.rb index 0c7954b5dee..3c93747eeee 100644 --- a/app/controllers/course/admin/codaveri_settings_controller.rb +++ b/app/controllers/course/admin/codaveri_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::CodaveriSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_codaveri_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/component_settings_controller.rb b/app/controllers/course/admin/component_settings_controller.rb index 609840f8ad0..d62c79f2c02 100644 --- a/app/controllers/course/admin/component_settings_controller.rb +++ b/app/controllers/course/admin/component_settings_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::Admin::ComponentSettingsController < Course::Admin::Controller before_action :load_settings - add_breadcrumb :edit, :course_admin_components_path def edit respond_to do |format| diff --git a/app/controllers/course/admin/controller.rb b/app/controllers/course/admin/controller.rb index 1f3e5c29394..2fdea0000b4 100644 --- a/app/controllers/course/admin/controller.rb +++ b/app/controllers/course/admin/controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::Controller < Course::ComponentController - add_breadcrumb :admin, :course_admin_path - before_action :authorize_admin private diff --git a/app/controllers/course/admin/discussion/topic_settings_controller.rb b/app/controllers/course/admin/discussion/topic_settings_controller.rb index 0678f719525..d0cecb1511d 100644 --- a/app/controllers/course/admin/discussion/topic_settings_controller.rb +++ b/app/controllers/course/admin/discussion/topic_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::Discussion::TopicSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_topics_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/forum_settings_controller.rb b/app/controllers/course/admin/forum_settings_controller.rb index fa1eadb6f23..222deea6d13 100644 --- a/app/controllers/course/admin/forum_settings_controller.rb +++ b/app/controllers/course/admin/forum_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::ForumSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_forums_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/leaderboard_settings_controller.rb b/app/controllers/course/admin/leaderboard_settings_controller.rb index 6c14bd1744e..f075af169d1 100644 --- a/app/controllers/course/admin/leaderboard_settings_controller.rb +++ b/app/controllers/course/admin/leaderboard_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::LeaderboardSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_leaderboard_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/lesson_plan_settings_controller.rb b/app/controllers/course/admin/lesson_plan_settings_controller.rb index b9d669b3a8b..8df7618cfde 100644 --- a/app/controllers/course/admin/lesson_plan_settings_controller.rb +++ b/app/controllers/course/admin/lesson_plan_settings_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::Admin::LessonPlanSettingsController < Course::Admin::Controller before_action :load_item_settings - add_breadcrumb :edit, :course_admin_lesson_plan_path def edit respond_to do |format| diff --git a/app/controllers/course/admin/material_settings_controller.rb b/app/controllers/course/admin/material_settings_controller.rb index 2b0085b92cf..0072946f2d5 100644 --- a/app/controllers/course/admin/material_settings_controller.rb +++ b/app/controllers/course/admin/material_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::MaterialSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_materials_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/notification_settings_controller.rb b/app/controllers/course/admin/notification_settings_controller.rb index 2b70214c2da..f6471212917 100644 --- a/app/controllers/course/admin/notification_settings_controller.rb +++ b/app/controllers/course/admin/notification_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::NotificationSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_notifications_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/sidebar_settings_controller.rb b/app/controllers/course/admin/sidebar_settings_controller.rb index dba124473c9..0477ed500ce 100644 --- a/app/controllers/course/admin/sidebar_settings_controller.rb +++ b/app/controllers/course/admin/sidebar_settings_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::Admin::SidebarSettingsController < Course::Admin::Controller before_action :load_settings - add_breadcrumb :index, :course_admin_sidebar_path def edit respond_to do |format| diff --git a/app/controllers/course/admin/video_settings_controller.rb b/app/controllers/course/admin/video_settings_controller.rb index 75acb38735f..000eb91f411 100644 --- a/app/controllers/course/admin/video_settings_controller.rb +++ b/app/controllers/course/admin/video_settings_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true class Course::Admin::VideoSettingsController < Course::Admin::Controller - add_breadcrumb :edit, :course_admin_videos_path - def edit respond_to do |format| format.html { render 'course/admin/index' } diff --git a/app/controllers/course/admin/videos/tabs_controller.rb b/app/controllers/course/admin/videos/tabs_controller.rb index 87f2f8a3096..88628305564 100644 --- a/app/controllers/course/admin/videos/tabs_controller.rb +++ b/app/controllers/course/admin/videos/tabs_controller.rb @@ -5,8 +5,6 @@ class Course::Admin::Videos::TabsController < Course::Admin::Controller through_association: :video_tabs, class: Course::Video::Tab.name - add_breadcrumb :index, :course_admin_videos_path - def new end diff --git a/app/controllers/course/announcements_controller.rb b/app/controllers/course/announcements_controller.rb index 5bf548ed6c1..c833809490d 100644 --- a/app/controllers/course/announcements_controller.rb +++ b/app/controllers/course/announcements_controller.rb @@ -3,7 +3,6 @@ class Course::AnnouncementsController < Course::ComponentController include Course::UsersHelper load_and_authorize_resource :announcement, through: :course, class: Course::Announcement.name - before_action :add_announcement_breadcrumb after_action :mark_announcements_as_read, only: [:index] @@ -19,7 +18,7 @@ def index def create if @announcement.save - render partial: 'announcements/announcement_list_data', + render partial: 'announcements/announcement_data', locals: { announcement: @announcement } else render json: { errors: @announcement.errors }, status: :bad_request @@ -28,7 +27,7 @@ def create def update if @announcement.update(announcement_params) - render partial: 'announcements/announcement_list_data', + render partial: 'announcements/announcement_data', locals: { announcement: @announcement }, status: :ok else @@ -50,10 +49,6 @@ def announcement_params params.require(:announcement).permit(:title, :content, :sticky, :start_at, :end_at) end - def add_announcement_breadcrumb - add_breadcrumb @settings.title || :index, :course_announcements_path - end - # @return [Course::AnnouncementsComponent] The announcement component. # @return [nil] If announcement component is disabled. def component diff --git a/app/controllers/course/assessment/component_controller.rb b/app/controllers/course/assessment/component_controller.rb index 1cab78f8bc0..b2aa1199d22 100644 --- a/app/controllers/course/assessment/component_controller.rb +++ b/app/controllers/course/assessment/component_controller.rb @@ -1,10 +1,3 @@ # frozen_string_literal: true class Course::Assessment::ComponentController < Course::Assessment::Controller - before_action :add_assessment_breadcrumb - - protected - - def add_assessment_breadcrumb - add_breadcrumb @assessment.title, course_assessment_path(current_course, @assessment) - end end diff --git a/app/controllers/course/assessment/controller.rb b/app/controllers/course/assessment/controller.rb index 58ce106c686..af864683e02 100644 --- a/app/controllers/course/assessment/controller.rb +++ b/app/controllers/course/assessment/controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Course::Assessment::Controller < Course::ComponentController before_action :load_and_authorize_assessment - before_action :add_assessment_breadcrumbs + before_action :load_category_and_tab protected @@ -21,26 +21,17 @@ def tab private + def load_category_and_tab + category + tab + end + def load_and_authorize_assessment options = load_assessment_options.reverse_merge(through: :course, class: Course::Assessment.name) self.class.cancan_resource_class.new(self, :assessment, options).load_and_authorize_resource end - def add_assessment_breadcrumbs - category_path = course_assessments_path(course_id: current_course, category: category) - add_breadcrumb(category.title, category_path) - - add_assessment_tab_breadcrumb - end - - def add_assessment_tab_breadcrumb - return if category.tabs.length == 1 - - tab_path = course_assessments_path(course_id: current_course, category: category, tab: tab) - add_breadcrumb(tab.title, tab_path) - end - # @return [Course::AssessmentsComponent] # @return [nil] If component is disabled. def component diff --git a/app/controllers/course/assessment/question_bundle_assignments_controller.rb b/app/controllers/course/assessment/question_bundle_assignments_controller.rb index 4010b9e4e46..378b01aec93 100644 --- a/app/controllers/course/assessment/question_bundle_assignments_controller.rb +++ b/app/controllers/course/assessment/question_bundle_assignments_controller.rb @@ -4,8 +4,6 @@ class Course::Assessment::QuestionBundleAssignmentsController < Course::Assessme load_and_authorize_resource :question_bundle_assignment, class: Course::Assessment::QuestionBundleAssignment, through: :assessment - before_action :add_breadcrumbs - def index @question_group_lookup = @assessment.question_groups.select(:id, :title).to_h { |qg| [qg.id, qg.title] } @question_bundle_lookup = @assessment.question_bundles.select(:id, :title).to_h { |qb| [qb.id, qb.title] } @@ -91,10 +89,4 @@ def past_assignments_hash [submission.creator_id, hash] end end - - def add_breadcrumbs - add_breadcrumb(@assessment.title, course_assessment_path(current_course, @assessment)) - add_breadcrumb('Question Bundle Assignment', - course_assessment_question_bundle_assignments_path(current_course, @assessment)) - end end diff --git a/app/controllers/course/assessment/question_bundle_questions_controller.rb b/app/controllers/course/assessment/question_bundle_questions_controller.rb index 0e914f4ea84..de08f0715e8 100644 --- a/app/controllers/course/assessment/question_bundle_questions_controller.rb +++ b/app/controllers/course/assessment/question_bundle_questions_controller.rb @@ -4,8 +4,6 @@ class Course::Assessment::QuestionBundleQuestionsController < Course::Assessment through: :assessment skip_load_resource :question_bundle_question, only: [:new, :create] - before_action :add_breadcrumbs - def index @question_bundle_questions = Course::Assessment::QuestionBundleQuestion.where(id: @question_bundle_questions). @@ -50,12 +48,6 @@ def destroy private - def add_breadcrumbs - add_breadcrumb(@assessment.title, course_assessment_path(current_course, @assessment)) - add_breadcrumb('Question Bundle Questions', - course_assessment_question_bundle_questions_path(current_course, @assessment)) - end - def question_bundle_question_params params.require(:question_bundle_question).permit(:weight, :bundle_id, :question_id) end diff --git a/app/controllers/course/assessment/question_bundles_controller.rb b/app/controllers/course/assessment/question_bundles_controller.rb index 2b250d49585..8a4a26adbb2 100644 --- a/app/controllers/course/assessment/question_bundles_controller.rb +++ b/app/controllers/course/assessment/question_bundles_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::Assessment::QuestionBundlesController < Course::Assessment::Controller load_and_authorize_resource :question_bundle, class: Course::Assessment::QuestionBundle, through: :assessment - before_action :add_breadcrumbs def index end @@ -40,11 +39,6 @@ def destroy private - def add_breadcrumbs - add_breadcrumb(@assessment.title, course_assessment_path(current_course, @assessment)) - add_breadcrumb('Question Bundles', course_assessment_question_bundles_path(current_course, @assessment)) - end - def question_bundle_params params.require(:question_bundle).permit(:title, :group_id) end diff --git a/app/controllers/course/assessment/question_groups_controller.rb b/app/controllers/course/assessment/question_groups_controller.rb index c89ffd2122a..81c4e8c3f35 100644 --- a/app/controllers/course/assessment/question_groups_controller.rb +++ b/app/controllers/course/assessment/question_groups_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::Assessment::QuestionGroupsController < Course::Assessment::Controller load_and_authorize_resource :question_group, class: Course::Assessment::QuestionGroup, through: :assessment - before_action :add_breadcrumbs def index @question_groups = @question_groups.order(:weight) @@ -40,11 +39,6 @@ def destroy private - def add_breadcrumbs - add_breadcrumb(@assessment.title, course_assessment_path(current_course, @assessment)) - add_breadcrumb('Question Groups', course_assessment_question_groups_path(current_course, @assessment)) - end - def question_group_params params.require(:question_group).permit(:title, :weight) end diff --git a/app/controllers/course/assessment/sessions_controller.rb b/app/controllers/course/assessment/sessions_controller.rb index 0c3977a9e7d..be7e495c850 100644 --- a/app/controllers/course/assessment/sessions_controller.rb +++ b/app/controllers/course/assessment/sessions_controller.rb @@ -3,8 +3,6 @@ class Course::Assessment::SessionsController < Course::Assessment::Controller before_action :load_and_authorize_submission def new - add_breadcrumb(@assessment.title, course_assessment_path(current_course, @assessment)) - add_breadcrumb :new end def create diff --git a/app/controllers/course/assessment/skill_branches_controller.rb b/app/controllers/course/assessment/skill_branches_controller.rb index 3717072773d..00e5ce0e767 100644 --- a/app/controllers/course/assessment/skill_branches_controller.rb +++ b/app/controllers/course/assessment/skill_branches_controller.rb @@ -3,7 +3,6 @@ class Course::Assessment::SkillBranchesController < Course::ComponentController load_and_authorize_resource :skill_branch, class: Course::Assessment::SkillBranch.name, through: :course, through_association: :assessment_skill_branches - add_breadcrumb :index, :course_assessments_skills_path def create if @skill_branch.save diff --git a/app/controllers/course/assessment/skills_controller.rb b/app/controllers/course/assessment/skills_controller.rb index f8acd377090..ec777344389 100644 --- a/app/controllers/course/assessment/skills_controller.rb +++ b/app/controllers/course/assessment/skills_controller.rb @@ -3,7 +3,6 @@ class Course::Assessment::SkillsController < Course::ComponentController load_and_authorize_resource :skill, class: Course::Assessment::Skill.name, through: :course, through_association: :assessment_skills before_action :load_skill_branches - add_breadcrumb :index, :course_assessments_skills_path def index @skills = @skills.includes(:skill_branch).group_by(&:skill_branch) diff --git a/app/controllers/course/assessment/submission/controller.rb b/app/controllers/course/assessment/submission/controller.rb index 174e9524cb0..29c0ff0f2f2 100644 --- a/app/controllers/course/assessment/submission/controller.rb +++ b/app/controllers/course/assessment/submission/controller.rb @@ -1,12 +1,4 @@ # frozen_string_literal: true class Course::Assessment::Submission::Controller < Course::Assessment::Controller - load_and_authorize_resource :submission, class: Course::Assessment::Submission.name, - through: :assessment - before_action :add_assessment_breadcrumb - - protected - - def add_assessment_breadcrumb - add_breadcrumb(@assessment.title, course_assessment_path(current_course, @assessment)) - end + load_and_authorize_resource :submission, class: Course::Assessment::Submission.name, through: :assessment end diff --git a/app/controllers/course/assessment/submissions_controller.rb b/app/controllers/course/assessment/submissions_controller.rb index 9cda88e5ea3..3e5356120ad 100644 --- a/app/controllers/course/assessment/submissions_controller.rb +++ b/app/controllers/course/assessment/submissions_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Course::Assessment::SubmissionsController < Course::ComponentController before_action :load_submissions - before_action :add_submissions_breadcrumb + before_action :load_category before_action :load_group_managers, only: [:pending, :index] def index @@ -59,6 +59,8 @@ def category end end + alias_method :load_category, :category + # Load student submissions. def load_submissions student_ids = if current_course_user&.student? @@ -99,10 +101,6 @@ def load_assessments end end - def add_submissions_breadcrumb - add_breadcrumb :index, course_submissions_path(current_course, category: category) - end - # @return [Course::AssessmentsComponent] # @return [nil] If component is disabled. def component diff --git a/app/controllers/course/controller.rb b/app/controllers/course/controller.rb index 0641b62fc45..328e924466d 100644 --- a/app/controllers/course/controller.rb +++ b/app/controllers/course/controller.rb @@ -4,8 +4,6 @@ class Course::Controller < ApplicationController before_action :set_last_active_at helper name - before_action :add_course_breadcrumb - # Gets the sidebar items. The sidebar items are ordered by the settings of current course. # # @param [Symbol] type The type of sidebar item, all sidebar items will be returned if the type @@ -77,11 +75,6 @@ def sidebar_items_weights defined_sidebar_settings.to_h { |item| [item.id, item.weight] } end - def add_course_breadcrumb - add_breadcrumb(current_course.title, course_path(current_course)) if - current_course.present? && current_course.id.present? - end - def set_last_active_at return if current_course.nil? || current_course_user.nil? diff --git a/app/controllers/course/courses_controller.rb b/app/controllers/course/courses_controller.rb index ebc1b102a05..f6bea291e2e 100644 --- a/app/controllers/course/courses_controller.rb +++ b/app/controllers/course/courses_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Course::CoursesController < Course::Controller include Course::ActivityFeedsConcern - skip_authorize_resource :course, only: [:show, :index] + skip_authorize_resource :course, only: [:show, :index, :sidebar] def index @courses = Course.publicly_accessible @@ -33,6 +33,9 @@ def create def destroy end + def sidebar + end + protected def publicly_accessible? diff --git a/app/controllers/course/discussion/topics_controller.rb b/app/controllers/course/discussion/topics_controller.rb index fc28a505c55..cd33aba9b54 100644 --- a/app/controllers/course/discussion/topics_controller.rb +++ b/app/controllers/course/discussion/topics_controller.rb @@ -5,7 +5,6 @@ class Course::Discussion::TopicsController < Course::ComponentController load_and_authorize_resource :discussion_topic, through: :course, instance_name: :topic, class: Course::Discussion::Topic.name, parent: false - before_action :add_topics_breadcrumb def index end @@ -93,10 +92,6 @@ def my_students_topics preload(:actable) end - def add_topics_breadcrumb - add_breadcrumb @settings.title || :index, :course_topics_path - end - def component current_component_host[:course_discussion_topics_component] end diff --git a/app/controllers/course/experience_points/disbursement_controller.rb b/app/controllers/course/experience_points/disbursement_controller.rb index 8ec5053da74..6e496961f0c 100644 --- a/app/controllers/course/experience_points/disbursement_controller.rb +++ b/app/controllers/course/experience_points/disbursement_controller.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true class Course::ExperiencePoints::DisbursementController < Course::ComponentController - include Course::UsersBreadcrumbConcern before_action :load_resource before_action :authorize_resource diff --git a/app/controllers/course/experience_points_records_controller.rb b/app/controllers/course/experience_points_records_controller.rb index 1822fc9e657..396d75e8c5d 100644 --- a/app/controllers/course/experience_points_records_controller.rb +++ b/app/controllers/course/experience_points_records_controller.rb @@ -1,11 +1,8 @@ # frozen_string_literal: true class Course::ExperiencePointsRecordsController < Course::ComponentController - include Course::UsersBreadcrumbConcern - load_resource :course_user, through: :course, id_param: :user_id load_and_authorize_resource :experience_points_record, through: :course_user, class: Course::ExperiencePointsRecord.name - before_action :add_breadcrumbs def index respond_to do |format| @@ -53,11 +50,6 @@ def experience_points_record_params params.require(:experience_points_record).permit(:points_awarded, :reason) end - def add_breadcrumbs - add_breadcrumb @course_user.name, course_user_path(current_course, @course_user) - add_breadcrumb :index - end - # @return [Course::ExperiencePointsComponent] # @return [nil] If component is disabled. def component diff --git a/app/controllers/course/forum/component_controller.rb b/app/controllers/course/forum/component_controller.rb index 9e5878c1086..e237666561d 100644 --- a/app/controllers/course/forum/component_controller.rb +++ b/app/controllers/course/forum/component_controller.rb @@ -1,9 +1,3 @@ # frozen_string_literal: true class Course::Forum::ComponentController < Course::Forum::Controller - protected - - def add_forum_breadcrumb - super - add_breadcrumb @forum.name, course_forum_path(current_course, @forum) - end end diff --git a/app/controllers/course/forum/controller.rb b/app/controllers/course/forum/controller.rb index d530809b3a7..45a4eb5a045 100644 --- a/app/controllers/course/forum/controller.rb +++ b/app/controllers/course/forum/controller.rb @@ -3,7 +3,6 @@ class Course::Forum::Controller < Course::ComponentController helper Course::Forum::ControllerHelper before_action :load_forum, unless: :skip_load_forum? authorize_resource :forum, class: Course::Forum.name - before_action :add_forum_breadcrumb private @@ -11,11 +10,6 @@ def load_forum @forum ||= current_course.forums.friendly.find(params[:forum_id] || params[:id]) end - def add_forum_breadcrumb - add_breadcrumb component.settings.title || t('breadcrumbs.course.forum.forums.index'), - :course_forums_path - end - # @return [Course::ForumsComponent] The forum component. # @return [nil] If component is disabled. def component diff --git a/app/controllers/course/forum/forums_controller.rb b/app/controllers/course/forum/forums_controller.rb index 974a9cb77b8..7fb1c85dcd6 100644 --- a/app/controllers/course/forum/forums_controller.rb +++ b/app/controllers/course/forum/forums_controller.rb @@ -2,7 +2,6 @@ class Course::Forum::ForumsController < Course::Forum::Controller include Course::UsersHelper load_resource :forum, class: Course::Forum.name, through: :course, only: [:index, :new, :create] - before_action :add_forum_item_breadcrumb def index respond_to do |format| @@ -120,10 +119,6 @@ def forum_params params.require(:forum).permit(:name, :description, :forum_topics_auto_subscribe, :course_id) end - def add_forum_item_breadcrumb - add_breadcrumb @forum.name, course_forum_path(current_course, @forum) if @forum&.persisted? - end - def skip_load_forum? [:index, :create, :all_posts, :search, :mark_all_as_read].include?(action_name.to_sym) end diff --git a/app/controllers/course/forum/posts_controller.rb b/app/controllers/course/forum/posts_controller.rb index 05cf0bf22d4..72be9b9555c 100644 --- a/app/controllers/course/forum/posts_controller.rb +++ b/app/controllers/course/forum/posts_controller.rb @@ -4,7 +4,6 @@ class Course::Forum::PostsController < Course::Forum::ComponentController authorize_resource :topic skip_authorize_resource :post, only: :toggle_answer before_action :authorize_locked_topic, only: [:create] - before_action :add_topic_breadcrumb include Course::Discussion::PostsConcern @@ -86,10 +85,6 @@ def load_topic @topic ||= @forum.topics.friendly.find(topic_id_param) end - def add_topic_breadcrumb - add_breadcrumb @topic.title, course_forum_topic_path(current_course, @forum, @topic) - end - def post_vote_param params.permit(:vote)[:vote].to_i end diff --git a/app/controllers/course/forum/topics_controller.rb b/app/controllers/course/forum/topics_controller.rb index 72e4af9f6b1..904298079ab 100644 --- a/app/controllers/course/forum/topics_controller.rb +++ b/app/controllers/course/forum/topics_controller.rb @@ -8,7 +8,6 @@ class Course::Forum::TopicsController < Course::Forum::ComponentController before_action :load_topic, except: [:create] load_resource :topic, class: Course::Forum::Topic.name, through: :forum, only: [:create] authorize_resource :topic, class: Course::Forum::Topic.name, except: [:set_resolved] - before_action :add_topic_breadcrumb after_action :mark_posts_read, only: [:show] def show @@ -72,13 +71,6 @@ def load_topic @topic ||= @forum.topics.friendly.find(params[:id]) end - def add_topic_breadcrumb - return unless @topic&.persisted? - - add_breadcrumb @topic.title, course_forum_topic_path(current_course, @forum, - @topic) - end - def mark_posts_read @topic.posts.klass.mark_as_read!(@topic.posts.select(&:persisted?), for: current_user) end diff --git a/app/controllers/course/group/group_categories_controller.rb b/app/controllers/course/group/group_categories_controller.rb index 5197d609d26..e3eba1118a7 100644 --- a/app/controllers/course/group/group_categories_controller.rb +++ b/app/controllers/course/group/group_categories_controller.rb @@ -3,7 +3,6 @@ class Course::Group::GroupCategoriesController < Course::ComponentController include Course::Group::GroupManagerConcern load_and_authorize_resource :group_category, class: Course::GroupCategory - before_action :add_group_breadcrumb def index respond_to do |format| @@ -124,10 +123,6 @@ def update_groups_params ]) end - def add_group_breadcrumb - add_breadcrumb :index, course_group_categories_path(current_course) - end - # @return [Course::GroupsComponent] # @return [nil] If component is disabled. def component diff --git a/app/controllers/course/leaderboards_controller.rb b/app/controllers/course/leaderboards_controller.rb index 402ac851458..9b6b2ca6c88 100644 --- a/app/controllers/course/leaderboards_controller.rb +++ b/app/controllers/course/leaderboards_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::LeaderboardsController < Course::ComponentController include Course::LeaderboardsHelper - before_action :add_leaderboard_breadcrumb before_action :check_component_settings before_action :preload_course_levels, only: [:index] before_action :fetch_course_users, only: [:index] @@ -31,10 +30,6 @@ def preload_course_levels @course.levels.to_a end - def add_leaderboard_breadcrumb - add_breadcrumb @settings.title || :index, :course_leaderboard_path - end - # @return [Course::LeaderboardComponent] The leaderboard component. # @return [nil] If leaderboard component is disabled. def component diff --git a/app/controllers/course/learning_map_controller.rb b/app/controllers/course/learning_map_controller.rb index aceb5922a42..a0752ebc407 100644 --- a/app/controllers/course/learning_map_controller.rb +++ b/app/controllers/course/learning_map_controller.rb @@ -5,7 +5,6 @@ class Course::LearningMapController < Course::ComponentController before_action :authorize_learning_map before_action :authorize_update, only: [:add_parent_node, :remove_parent_node, :toggle_satisfiability_type] - add_breadcrumb :index, :course_learning_map_path def index respond_to do |format| diff --git a/app/controllers/course/lesson_plan/controller.rb b/app/controllers/course/lesson_plan/controller.rb index 2c81dfde1a9..0162000d0f9 100644 --- a/app/controllers/course/lesson_plan/controller.rb +++ b/app/controllers/course/lesson_plan/controller.rb @@ -2,8 +2,6 @@ class Course::LessonPlan::Controller < Course::ComponentController include Course::LessonPlan::ActsAsLessonPlanItemConcern - add_breadcrumb :index, :course_lesson_plan_path - private # Define lesson plan component for the check whether the component is defined. diff --git a/app/controllers/course/levels_controller.rb b/app/controllers/course/levels_controller.rb index 13182e73d50..3f0b94b0d71 100644 --- a/app/controllers/course/levels_controller.rb +++ b/app/controllers/course/levels_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Course::LevelsController < Course::ComponentController load_and_authorize_resource :level, through: :course, class: Course::Level.name - add_breadcrumb :index, :course_levels_path def index end diff --git a/app/controllers/course/material/controller.rb b/app/controllers/course/material/controller.rb index 3d066f8db4c..a7b933ed1b3 100644 --- a/app/controllers/course/material/controller.rb +++ b/app/controllers/course/material/controller.rb @@ -3,8 +3,6 @@ class Course::Material::Controller < Course::ComponentController load_and_authorize_resource :folder, through: :course, through_association: :material_folders, class: Course::Material::Folder.name - before_action :add_folder_breadcrumb - private # @return [Course::MaterialsComponent] The materials component. @@ -14,16 +12,6 @@ def component end helper_method :component - def add_folder_breadcrumb - folders_chain = @folder.ancestors.reverse << @folder - root_folder = folders_chain.shift - - add_breadcrumb root_folder_name, course_material_folder_path(current_course, root_folder) - folders_chain.each do |folder| - add_breadcrumb folder.name, course_material_folder_path(current_course, folder) - end - end - def root_folder_name component.settings.title || t('course.material.sidebar_title') end diff --git a/app/controllers/course/survey/controller.rb b/app/controllers/course/survey/controller.rb index 99b9a72c757..a3048ab1d70 100644 --- a/app/controllers/course/survey/controller.rb +++ b/app/controllers/course/survey/controller.rb @@ -3,7 +3,6 @@ class Course::Survey::Controller < Course::ComponentController include Course::LessonPlan::ActsAsLessonPlanItemConcern load_and_authorize_resource :survey, through: :course, class: Course::Survey.name - add_breadcrumb :index, :course_surveys_path private diff --git a/app/controllers/course/user_email_subscriptions_controller.rb b/app/controllers/course/user_email_subscriptions_controller.rb index 4617037074e..2a2fadb66c7 100644 --- a/app/controllers/course/user_email_subscriptions_controller.rb +++ b/app/controllers/course/user_email_subscriptions_controller.rb @@ -1,11 +1,7 @@ # frozen_string_literal: true class Course::UserEmailSubscriptionsController < Course::ComponentController - include Course::UsersBreadcrumbConcern - load_resource :course_user, through: :course, id_param: :user_id - before_action :add_breadcrumbs - def edit authorize!(:manage, Course::UserEmailUnsubscription.new(course_user: @course_user)) load_subscription_settings @@ -24,11 +20,6 @@ def update private - def add_breadcrumbs - add_breadcrumb @course_user.name, course_user_path(current_course, @course_user) - add_breadcrumb :index - end - def email_setting_params params.require(:user_email_subscriptions).permit(:component, :course_assessment_category_id, :setting) end diff --git a/app/controllers/course/user_invitations_controller.rb b/app/controllers/course/user_invitations_controller.rb index 095a03f553e..9228b3c6a54 100644 --- a/app/controllers/course/user_invitations_controller.rb +++ b/app/controllers/course/user_invitations_controller.rb @@ -3,7 +3,6 @@ class Course::UserInvitationsController < Course::ComponentController before_action :authorize_invitation! load_resource :invitation, through: :course, class: Course::UserInvitation, parent: false, only: :destroy - add_breadcrumb :index, :course_users_students_path def index respond_to do |format| diff --git a/app/controllers/course/users_controller.rb b/app/controllers/course/users_controller.rb index 35424d7fa92..32b8bd22740 100644 --- a/app/controllers/course/users_controller.rb +++ b/app/controllers/course/users_controller.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true class Course::UsersController < Course::ComponentController - include Course::UsersBreadcrumbConcern include Course::UsersControllerManagementConcern before_action :load_resource diff --git a/app/controllers/course/video/controller.rb b/app/controllers/course/video/controller.rb index 7884b27c243..cc129ea8c1d 100644 --- a/app/controllers/course/video/controller.rb +++ b/app/controllers/course/video/controller.rb @@ -3,7 +3,6 @@ class Course::Video::Controller < Course::ComponentController include Course::LessonPlan::ActsAsLessonPlanItemConcern load_and_authorize_resource :video, through: :course, class: Course::Video.name - before_action :add_videos_breadcrumb private @@ -11,19 +10,6 @@ def current_tab raise NotImplementedError end - def add_videos_breadcrumb - add_breadcrumb @settings.title || t('breadcrumbs.course.video.videos.index'), - :course_videos_path - - add_tabs_breadcrumb - end - - def add_tabs_breadcrumb - return if current_course.video_tabs.count == 1 || current_tab.blank? - - add_breadcrumb current_tab.title, course_videos_path(current_course, tab: current_tab) - end - # @return [Course::Video Component] The video component. # @return [nil] If video component is disabled. def component diff --git a/app/controllers/course/video/submission/controller.rb b/app/controllers/course/video/submission/controller.rb index 7ecffd8c17d..183da0ec6b5 100644 --- a/app/controllers/course/video/submission/controller.rb +++ b/app/controllers/course/video/submission/controller.rb @@ -1,11 +1,4 @@ # frozen_string_literal: true class Course::Video::Submission::Controller < Course::Video::Controller load_and_authorize_resource :submission, class: Course::Video::Submission.name, through: :video - before_action :add_video_breadcrumb - - protected - - def add_video_breadcrumb - add_breadcrumb(@video.title, course_video_path(current_course, @video)) - end end diff --git a/app/controllers/course/video/submission/submissions_controller.rb b/app/controllers/course/video/submission/submissions_controller.rb index e768dfcbe7a..1c1798c48dc 100644 --- a/app/controllers/course/video/submission/submissions_controller.rb +++ b/app/controllers/course/video/submission/submissions_controller.rb @@ -6,9 +6,7 @@ class Course::Video::Submission::SubmissionsController < Course::Video::Submissi def index respond_to do |format| - format.html do - add_breadcrumb t('.submissions') - end + format.html format.json do @submissions = @submissions.includes([{ experience_points_record: :course_user }, :statistic]) @my_students = current_course_user.try(:my_students) || [] diff --git a/app/controllers/course/video/videos_controller.rb b/app/controllers/course/video/videos_controller.rb index 0f84393416b..83a77598111 100644 --- a/app/controllers/course/video/videos_controller.rb +++ b/app/controllers/course/video/videos_controller.rb @@ -26,10 +26,7 @@ def index def show respond_to do |format| - format.html do - add_breadcrumb @video.title - render 'index' - end + format.html { render 'index' } format.json { render 'show' } end end diff --git a/app/controllers/course/video_submissions_controller.rb b/app/controllers/course/video_submissions_controller.rb index 0238c691b6b..1fc571faed4 100644 --- a/app/controllers/course/video_submissions_controller.rb +++ b/app/controllers/course/video_submissions_controller.rb @@ -1,11 +1,8 @@ # frozen_string_literal: true class Course::VideoSubmissionsController < Course::ComponentController - include Course::UsersBreadcrumbConcern - load_resource :course_user, through: :course, id_param: :user_id before_action :authorize_analyze_video! before_action :load_video_submissions - before_action :add_breadcrumbs def index @videos = @course.videos.ordered_by_date_and_title @@ -16,11 +13,6 @@ def index private - def add_breadcrumbs - add_breadcrumb @course_user.name, course_user_path(current_course, @course_user) - add_breadcrumb :index - end - # @return [Course::VideosComponent] # @return [nil] If component is disabled. def component diff --git a/app/controllers/instance_user_role_requests_controller.rb b/app/controllers/instance_user_role_requests_controller.rb index 5f60872112f..12ed48e9ec3 100644 --- a/app/controllers/instance_user_role_requests_controller.rb +++ b/app/controllers/instance_user_role_requests_controller.rb @@ -3,8 +3,6 @@ class InstanceUserRoleRequestsController < ApplicationController load_and_authorize_resource :user_role_request, through: :current_tenant, parent: false, class: ::Instance::UserRoleRequest.name def index - add_breadcrumb current_tenant.name, :admin_instance_admin_path - add_breadcrumb :index, :instance_user_role_requests_path @user_role_requests = @user_role_requests.includes(:confirmer, :user) respond_to do |format| diff --git a/app/controllers/system/admin/announcements_controller.rb b/app/controllers/system/admin/announcements_controller.rb index 5225a156a88..078a37a92a0 100644 --- a/app/controllers/system/admin/announcements_controller.rb +++ b/app/controllers/system/admin/announcements_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class System::Admin::AnnouncementsController < System::Admin::Controller load_and_authorize_resource :announcement, class: System::Announcement.name - add_breadcrumb :index, :admin_announcements_path def index respond_to do |format| @@ -14,7 +13,7 @@ def index def create if @announcement.save - render partial: 'announcements/announcement_list_data', + render partial: 'announcements/announcement_data', locals: { announcement: @announcement }, status: :ok else @@ -24,7 +23,7 @@ def create def update if @announcement.update(announcement_params) - render partial: 'announcements/announcement_list_data', + render partial: 'announcements/announcement_data', locals: { announcement: @announcement.reload }, status: :ok else diff --git a/app/controllers/system/admin/controller.rb b/app/controllers/system/admin/controller.rb index 4b1e25769be..09434f1e216 100644 --- a/app/controllers/system/admin/controller.rb +++ b/app/controllers/system/admin/controller.rb @@ -7,6 +7,4 @@ class System::Admin::Controller < ApplicationController def authorize_admin authorize!(:manage, :all) end - - add_breadcrumb :index, :admin_path end diff --git a/app/controllers/system/admin/courses_controller.rb b/app/controllers/system/admin/courses_controller.rb index af23aa1ed87..8686d87a424 100644 --- a/app/controllers/system/admin/courses_controller.rb +++ b/app/controllers/system/admin/courses_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class System::Admin::CoursesController < System::Admin::Controller around_action :unscope_resources - add_breadcrumb :index, :admin_courses_path def index respond_to do |format| diff --git a/app/controllers/system/admin/instance/announcements_controller.rb b/app/controllers/system/admin/instance/announcements_controller.rb index 16319f1766f..f1451240c6a 100644 --- a/app/controllers/system/admin/instance/announcements_controller.rb +++ b/app/controllers/system/admin/instance/announcements_controller.rb @@ -2,7 +2,6 @@ class System::Admin::Instance::AnnouncementsController < System::Admin::Instance::Controller load_and_authorize_resource :announcement, through: :current_tenant, parent: false, class: ::Instance::Announcement.name - add_breadcrumb :index, :admin_instance_announcements_path def index respond_to do |format| @@ -15,7 +14,7 @@ def index def create if @announcement.save - render partial: 'announcements/announcement_list_data', + render partial: 'announcements/announcement_data', locals: { announcement: @announcement }, status: :ok else @@ -25,7 +24,7 @@ def create def update if @announcement.update(announcement_params) - render partial: 'announcements/announcement_list_data', + render partial: 'announcements/announcement_data', locals: { announcement: @announcement.reload }, status: :ok else diff --git a/app/controllers/system/admin/instance/components_controller.rb b/app/controllers/system/admin/instance/components_controller.rb index 339ae66f203..e74373cc11d 100644 --- a/app/controllers/system/admin/instance/components_controller.rb +++ b/app/controllers/system/admin/instance/components_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class System::Admin::Instance::ComponentsController < System::Admin::Instance::Controller before_action :settings - add_breadcrumb :edit, :admin_instance_components_path def index respond_to do |format| diff --git a/app/controllers/system/admin/instance/controller.rb b/app/controllers/system/admin/instance/controller.rb index df0fd3b6ecd..024f11e4aa8 100644 --- a/app/controllers/system/admin/instance/controller.rb +++ b/app/controllers/system/admin/instance/controller.rb @@ -2,7 +2,6 @@ class System::Admin::Instance::Controller < ApplicationController before_action :load_instance before_action :authorize_instance_admin - before_action :add_instance_breadcrumb private @@ -13,8 +12,4 @@ def load_instance def authorize_instance_admin authorize!(:show, @instance) end - - def add_instance_breadcrumb - add_breadcrumb @instance.name, :admin_instance_admin_path - end end diff --git a/app/controllers/system/admin/instance/courses_controller.rb b/app/controllers/system/admin/instance/courses_controller.rb index 471d13dfeab..e395a57e646 100644 --- a/app/controllers/system/admin/instance/courses_controller.rb +++ b/app/controllers/system/admin/instance/courses_controller.rb @@ -2,8 +2,6 @@ class System::Admin::Instance::CoursesController < System::Admin::Instance::Controller load_and_authorize_resource :course, through: :instance - add_breadcrumb :index, :admin_instance_courses_path - def index respond_to do |format| format.html { render 'system/admin/instance/admin/index' } diff --git a/app/controllers/system/admin/instance/user_invitations_controller.rb b/app/controllers/system/admin/instance/user_invitations_controller.rb index e70d493184a..61c67acf60b 100644 --- a/app/controllers/system/admin/instance/user_invitations_controller.rb +++ b/app/controllers/system/admin/instance/user_invitations_controller.rb @@ -3,7 +3,6 @@ class System::Admin::Instance::UserInvitationsController < System::Admin::Instan load_and_authorize_resource :instance_user, class: InstanceUser.name, parent: false, except: [:new, :create, :destroy] - add_breadcrumb :index, :admin_instance_users_path def index @invitations = @instance.invitations.order(name: :asc) diff --git a/app/controllers/system/admin/instance/users_controller.rb b/app/controllers/system/admin/instance/users_controller.rb index 644b9a6e7a3..82700ea812c 100644 --- a/app/controllers/system/admin/instance/users_controller.rb +++ b/app/controllers/system/admin/instance/users_controller.rb @@ -2,7 +2,6 @@ class System::Admin::Instance::UsersController < System::Admin::Instance::Controller load_and_authorize_resource :instance_user, class: InstanceUser.name, parent: false, except: [:index] - add_breadcrumb :index, :admin_instance_users_path def index respond_to do |format| diff --git a/app/controllers/system/admin/instances_controller.rb b/app/controllers/system/admin/instances_controller.rb index 6e57a5de3be..6541a0d355e 100644 --- a/app/controllers/system/admin/instances_controller.rb +++ b/app/controllers/system/admin/instances_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class System::Admin::InstancesController < System::Admin::Controller load_and_authorize_resource :instance, class: ::Instance.name - add_breadcrumb :index, :admin_instances_path def index respond_to do |format| diff --git a/app/controllers/system/admin/users_controller.rb b/app/controllers/system/admin/users_controller.rb index 37f855c9530..13eec4aaa22 100644 --- a/app/controllers/system/admin/users_controller.rb +++ b/app/controllers/system/admin/users_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class System::Admin::UsersController < System::Admin::Controller load_and_authorize_resource :user, class: User.name - add_breadcrumb :index, :admin_users_path def index respond_to do |format| diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1f886af09e5..ccd79bdb51f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -11,7 +11,6 @@ module ApplicationHelper include ApplicationNotificationsHelper include ApplicationFormattersHelper - include ApplicationSidebarHelper include RouteOverridesHelper include FormForWithResourceHelper include RenderWithinLayoutHelper diff --git a/app/helpers/application_sidebar_helper.rb b/app/helpers/application_sidebar_helper.rb deleted file mode 100644 index f05227d38c8..00000000000 --- a/app/helpers/application_sidebar_helper.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true -# Helpers for displaying sidebars in pages. -module ApplicationSidebarHelper - # Checks if the current page has a sidebar. - # - # @return [Boolean] True if there is a sidebar for the current page. - def sidebar? - content_for?(:layout_sidebar) - end - - # Sets the current page to have a sidebar. - # - # The view must still render the sidebar in its content area. - def sidebar! - content_for(:layout_sidebar) do - 'true' - end - end - - # Defines the sidebar for the current page. - # - # @param [Array] classes An array of classes to apply to the sidebar container. - # @return [String] The buffer containing the markup for the sidebar. - def sidebar(classes: ['col-lg-2', 'col-md-3', 'col-sm-4'], &block) - sidebar! - - render layout: 'layouts/sidebar', locals: { sidebar_classes: classes }, &block - end - - # Display the given sidebar items. - # - # @param [Array] items An array of sidebar items to be displayed. - # @param [Array] classes An array of classes to apply to the sidebar items container. - # @return [String] The HTML string which will display the sidebar items. - def sidebar_items(items, classes: ['nav', 'nav-pills', 'nav-stacked']) - sidebar! - - links = items.map { |item| link_to_sidebar_item(item) } - content_tag(:ul, class: classes) do - links.map { |link| concat(content_tag(:li, link)) } - end - end - - # Generates the link to the given sidebar item. - # - # @param item The sidebar item. - # @return [String] The HTML string which will display the link. - def link_to_sidebar_item(item) - link_to(item[:path]) do - concat( - content_tag(:span, class: ['nav-icons']) do - fa_icon item[:icon] - end - ) - concat(item[:title]) - concat( - content_tag(:span, class: ['unread']) do - badge(item[:unread]) if item[:unread] && item[:unread] > 0 - end - ) - end - end -end diff --git a/app/helpers/application_theming_helper.rb b/app/helpers/application_theming_helper.rb index 728668a65be..80f0e1b9c21 100644 --- a/app/helpers/application_theming_helper.rb +++ b/app/helpers/application_theming_helper.rb @@ -4,11 +4,25 @@ def application_resources # Used to include external javascript/css files, Currently it's empty end + # TODO: Remove this once fully SPA def page_class return nil if content_for?(:page_class_specified) page_class = super content_for(:page_class_specified) { page_class } - page_class + @page_class = page_class + end + + # TODO: Remove this once fully SPA + def rails_page? + class_identifier = page_class.split.first + + [ + 'high-voltage-pages', + 'user-sessions', + 'user-registrations', + 'devise-passwords', + 'devise-confirmations' + ].include? class_identifier end end diff --git a/app/helpers/course/controller_helper.rb b/app/helpers/course/controller_helper.rb index c9c87e0b929..007bc6341b2 100644 --- a/app/helpers/course/controller_helper.rb +++ b/app/helpers/course/controller_helper.rb @@ -64,30 +64,10 @@ def link_to_user(user, options = {}, &block) end end - # Display the course_user_badge given the course_user - # - # @param [CourseUser] course_user The CourseUser for which the badge is to be displayed - # @return [String] HTML string to render the course_user_badge - def display_course_user_badge(course_user) - render partial: 'layouts/course_user_badge', - locals: { course_user: course_user } - end - def url_to_course_logo(course) asset_url(course_logo_local_url(course)) end - # Helper method to display the course logo. If logo is present, returns the medium version - # of the logo (see ImageUploader for more versions). Otherwise, returns the default course - # logo. - # - # @return [String] HTML string to render the course logo - def display_course_logo(course) - content_tag(:span, class: ['image']) do - image_tag(course_logo_local_url(course)) - end - end - def url_to_material(course, folder, material) course_material_folder_material_path(course, folder, material) end diff --git a/app/themes/default/assets/stylesheets/default/all.scss.erb b/app/themes/default/assets/stylesheets/default/all.scss.erb index 49c30ff88b3..36b330cdb5e 100644 --- a/app/themes/default/assets/stylesheets/default/all.scss.erb +++ b/app/themes/default/assets/stylesheets/default/all.scss.erb @@ -3,7 +3,8 @@ // they'll appear at the top of the compiled file, but it's generally better to create a new file // per style scope. // -@import 'default/layout'; +@import 'application'; +@import 'pygments-css/github'; <% # Import the rest of the files; @import '**/*' will include application.scss multiple times. # This is not perfect because every time a new file is added all assets need to be cleaned for the diff --git a/app/themes/default/assets/stylesheets/default/layout.scss b/app/themes/default/assets/stylesheets/default/layout.scss deleted file mode 100644 index 96050ae086d..00000000000 --- a/app/themes/default/assets/stylesheets/default/layout.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import 'application'; -@import 'pygments-css/github'; - -body { - padding-top: $navbar-height + $navbar-margin-bottom; -} - -.navbar { - .courses-menu { - max-height: 70vh; - overflow-y: scroll; - } -} diff --git a/app/themes/default/views/layouts/default.html.slim b/app/themes/default/views/layouts/default.html.slim index 93651dbb6b2..de90559f80e 100644 --- a/app/themes/default/views/layouts/default.html.slim +++ b/app/themes/default/views/layouts/default.html.slim @@ -4,6 +4,7 @@ html title = page_title meta http-equiv="X-UA-Compatible" content="IE=edge" + meta name="status" content=response.status = server_context_meta_tag = viewport_meta_tag = application_resources @@ -15,9 +16,13 @@ html = render 'layouts/favicon' body - = render 'layouts/topbar' - div#root.container-fluid - = global_announcements - = flash_messages - div class=page_class + - if rails_page? + = render 'layouts/topbar' + div#root.container-fluid + = global_announcements + = flash_messages + div class=@page_class + = yield + - else + div#root = yield diff --git a/app/views/announcements/_announcement_data.json.jbuilder b/app/views/announcements/_announcement_data.json.jbuilder new file mode 100644 index 00000000000..c66b45c08dd --- /dev/null +++ b/app/views/announcements/_announcement_data.json.jbuilder @@ -0,0 +1,21 @@ +# frozen_string_literal: true +json.partial! 'announcements/announcement_list_data', announcement: announcement + +json.endTime announcement.end_at + +user_or_course_user = local_assigns[:course_user] || announcement.creator + +json.creator do + json.id user_or_course_user.id + json.name user_or_course_user.name + json.userUrl url_to_user_or_course_user(@course, user_or_course_user) +end + +json.isUnread !user_signed_in? || announcement.unread?(current_user) +json.isSticky announcement.sticky? +json.isCurrentlyActive announcement.currently_active? + +json.permissions do + json.canEdit can?(:edit, announcement) + json.canDelete can?(:destroy, announcement) +end diff --git a/app/views/announcements/_announcement_list_data.json.jbuilder b/app/views/announcements/_announcement_list_data.json.jbuilder index c9d51010c52..71a3a373656 100644 --- a/app/views/announcements/_announcement_list_data.json.jbuilder +++ b/app/views/announcements/_announcement_list_data.json.jbuilder @@ -1,24 +1,6 @@ # frozen_string_literal: true - json.id announcement.id json.title announcement.title json.content format_ckeditor_rich_text(announcement.content) json.startTime announcement.start_at -json.endTime announcement.end_at - -user_or_course_user = local_assigns[:course_user] || announcement.creator - -json.creator do - json.id user_or_course_user.id - json.name user_or_course_user.name - json.userUrl url_to_user_or_course_user(@course, user_or_course_user) -end - -json.isUnread announcement.unread?(current_user) -json.isSticky announcement.sticky? -json.isCurrentlyActive announcement.currently_active? - -json.permissions do - json.canEdit can?(:edit, announcement) - json.canDelete can?(:destroy, announcement) -end +json.markAsReadUrl announcement_mark_as_read_path(announcement) diff --git a/app/views/announcements/index.json.jbuilder b/app/views/announcements/index.json.jbuilder index 8d026835448..98e8c837a18 100644 --- a/app/views/announcements/index.json.jbuilder +++ b/app/views/announcements/index.json.jbuilder @@ -1,5 +1,5 @@ # frozen_string_literal: true json.announcements @announcements do |announcement| - json.partial! 'announcements/announcement_list_data', announcement: announcement + json.partial! 'announcements/announcement_data', announcement: announcement end diff --git a/app/views/course/admin/sidebar_settings/edit.json.jbuilder b/app/views/course/admin/sidebar_settings/edit.json.jbuilder index 8721df2fcd0..bfdcd9cd1f3 100644 --- a/app/views/course/admin/sidebar_settings/edit.json.jbuilder +++ b/app/views/course/admin/sidebar_settings/edit.json.jbuilder @@ -5,5 +5,5 @@ json.array! sorted_sidebar_items do |item| json.id item.id json.title item.title json.weight item.weight - json.iconClassName "fa fa-#{item.icon}" + json.icon item.icon end diff --git a/app/views/course/announcements/index.json.jbuilder b/app/views/course/announcements/index.json.jbuilder index 0cb679597ae..fd92396add7 100644 --- a/app/views/course/announcements/index.json.jbuilder +++ b/app/views/course/announcements/index.json.jbuilder @@ -1,7 +1,7 @@ # frozen_string_literal: true json.announcements @announcements do |announcement| - json.partial! 'announcements/announcement_list_data', + json.partial! 'announcements/announcement_data', announcement: announcement, course_user: @course_users_hash[announcement.creator_id] end diff --git a/app/views/course/assessment/assessments/_achievement_badges.json.jbuilder b/app/views/course/assessment/assessments/_achievement_badges.json.jbuilder new file mode 100644 index 00000000000..d564a6609c0 --- /dev/null +++ b/app/views/course/assessment/assessments/_achievement_badges.json.jbuilder @@ -0,0 +1,6 @@ +# frozen_string_literal: true +json.array! achievements do |achievement| + json.url course_achievement_path(course, achievement) + json.badgeUrl achievement_badge_path(achievement) + json.title achievement.title +end diff --git a/app/views/course/assessment/assessments/authenticate.json.jbuilder b/app/views/course/assessment/assessments/authenticate.json.jbuilder index 9a878a3df1a..cc3b172cc33 100644 --- a/app/views/course/assessment/assessments/authenticate.json.jbuilder +++ b/app/views/course/assessment/assessments/authenticate.json.jbuilder @@ -1,6 +1,9 @@ # frozen_string_literal: true json.id @assessment.id +json.title @assessment.title +json.tabTitle "#{@category.title}: #{@tab.title}" +json.tabUrl course_assessments_path(course_id: current_course, category: @category, tab: @tab) json.isAuthenticated false json.isStartTimeBegin !assessment_not_started(@assessment_time) json.startAt @assessment_time.start_at diff --git a/app/views/course/assessment/assessments/edit.html.slim b/app/views/course/assessment/assessments/edit.html.slim index 52da0dce0a4..a777c8100dc 100644 --- a/app/views/course/assessment/assessments/edit.html.slim +++ b/app/views/course/assessment/assessments/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb format_inline_text(@assessment.title) div#app-root diff --git a/app/views/course/assessment/assessments/index.json.jbuilder b/app/views/course/assessment/assessments/index.json.jbuilder index fe55c1e24c1..f6afc62cf6d 100644 --- a/app/views/course/assessment/assessments/index.json.jbuilder +++ b/app/views/course/assessment/assessments/index.json.jbuilder @@ -23,6 +23,8 @@ json.display do end json.tabId @tab.id + json.tabTitle "#{@category.title}: #{@tab.title}" + json.tabUrl course_assessments_path(course_id: current_course, category: @category, tab: @tab) end json.assessments @assessments do |assessment| @@ -48,10 +50,8 @@ json.assessments @assessments do |assessment| achievement_conditionals = @conditional_service.achievement_conditional_for(assessment) top_conditionals = achievement_conditionals.first(3) - json.topConditionals top_conditionals do |achievement| - json.url course_achievement_path(current_course, achievement) - json.badgeUrl achievement_badge_path(achievement) - json.title achievement.title + json.topConditionals do + json.partial! 'achievement_badges', achievements: top_conditionals, course: current_course end conditionals_count = achievement_conditionals.size diff --git a/app/views/course/assessment/assessments/monitoring.html.slim b/app/views/course/assessment/assessments/monitoring.html.slim index acf71641a32..a777c8100dc 100644 --- a/app/views/course/assessment/assessments/monitoring.html.slim +++ b/app/views/course/assessment/assessments/monitoring.html.slim @@ -1,3 +1 @@ -- add_breadcrumb @assessment.title, course_assessment_path(current_course, @assessment) -- add_breadcrumb I18n.t('layouts.monitoring.pulsegrid') -div#app-root.dangerously-fit-height-to-viewport +div#app-root diff --git a/app/views/course/assessment/assessments/show.html.slim b/app/views/course/assessment/assessments/show.html.slim index 9e824575343..72c4657f683 100644 --- a/app/views/course/assessment/assessments/show.html.slim +++ b/app/views/course/assessment/assessments/show.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb format_inline_text(@assessment.title) / Randomized Assessment is temporarily hidden (PR#5406) / - if @assessment.randomization.present? / = render 'assessment_question_bundle_buttons', assessment: @assessment diff --git a/app/views/course/assessment/assessments/show.json.jbuilder b/app/views/course/assessment/assessments/show.json.jbuilder index c25dafba1b3..c724cdb987b 100644 --- a/app/views/course/assessment/assessments/show.json.jbuilder +++ b/app/views/course/assessment/assessments/show.json.jbuilder @@ -12,6 +12,8 @@ can_manage = can?(:manage, assessment) json.id assessment.id json.title assessment.title +json.tabTitle "#{@category.title}: #{@tab.title}" +json.tabUrl course_assessments_path(course_id: current_course, category: @category, tab: @tab) json.description assessment.description unless @assessment.description.blank? json.autograded assessment.autograded? json.hasTodo assessment.has_todo if can_manage diff --git a/app/views/course/assessment/assessments/statistics.html.slim b/app/views/course/assessment/assessments/statistics.html.slim index 91901776da6..a777c8100dc 100644 --- a/app/views/course/assessment/assessments/statistics.html.slim +++ b/app/views/course/assessment/assessments/statistics.html.slim @@ -1,3 +1 @@ -- add_breadcrumb format_inline_text(@assessment.title) - div#app-root diff --git a/app/views/course/assessment/question/forum_post_responses/edit.html.slim b/app/views/course/assessment/question/forum_post_responses/edit.html.slim index 9d86c9e24b2..a777c8100dc 100644 --- a/app/views/course/assessment/question/forum_post_responses/edit.html.slim +++ b/app/views/course/assessment/question/forum_post_responses/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb format_inline_text(@question_assessment.display_title) div#app-root diff --git a/app/views/course/assessment/question/forum_post_responses/new.html.slim b/app/views/course/assessment/question/forum_post_responses/new.html.slim index bdb6d2d746b..a777c8100dc 100644 --- a/app/views/course/assessment/question/forum_post_responses/new.html.slim +++ b/app/views/course/assessment/question/forum_post_responses/new.html.slim @@ -1,2 +1 @@ -- add_breadcrumb t('.header') div#app-root diff --git a/app/views/course/assessment/question/multiple_responses/edit.html.slim b/app/views/course/assessment/question/multiple_responses/edit.html.slim index 9d86c9e24b2..a777c8100dc 100644 --- a/app/views/course/assessment/question/multiple_responses/edit.html.slim +++ b/app/views/course/assessment/question/multiple_responses/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb format_inline_text(@question_assessment.display_title) div#app-root diff --git a/app/views/course/assessment/question/multiple_responses/new.html.slim b/app/views/course/assessment/question/multiple_responses/new.html.slim index a70d638c029..a777c8100dc 100644 --- a/app/views/course/assessment/question/multiple_responses/new.html.slim +++ b/app/views/course/assessment/question/multiple_responses/new.html.slim @@ -1,2 +1 @@ -- add_breadcrumb @multiple_response_question.multiple_choice? ? t('.multiple_choice_header') : t('.multiple_response_header') div#app-root diff --git a/app/views/course/assessment/question/programming/edit.html.slim b/app/views/course/assessment/question/programming/edit.html.slim index 9d86c9e24b2..a777c8100dc 100644 --- a/app/views/course/assessment/question/programming/edit.html.slim +++ b/app/views/course/assessment/question/programming/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb format_inline_text(@question_assessment.display_title) div#app-root diff --git a/app/views/course/assessment/question/programming/new.html.slim b/app/views/course/assessment/question/programming/new.html.slim index 5128d0f4143..a777c8100dc 100644 --- a/app/views/course/assessment/question/programming/new.html.slim +++ b/app/views/course/assessment/question/programming/new.html.slim @@ -1,2 +1 @@ -- add_breadcrumb :new div#app-root diff --git a/app/views/course/assessment/question/scribing/edit.html.slim b/app/views/course/assessment/question/scribing/edit.html.slim index f07615f8b52..a777c8100dc 100644 --- a/app/views/course/assessment/question/scribing/edit.html.slim +++ b/app/views/course/assessment/question/scribing/edit.html.slim @@ -1,3 +1 @@ -- add_breadcrumb format_inline_text(@question_assessment.display_title) -= page_header format_inline_text(@question_assessment.display_title) div#app-root diff --git a/app/views/course/assessment/question/scribing/new.html.slim b/app/views/course/assessment/question/scribing/new.html.slim index 10f2918fc1a..a777c8100dc 100644 --- a/app/views/course/assessment/question/scribing/new.html.slim +++ b/app/views/course/assessment/question/scribing/new.html.slim @@ -1,4 +1 @@ -- add_breadcrumb :new -- template = 'course/assessment/question/scribing/scribing_question.json.jbuilder' -= page_header div#app-root diff --git a/app/views/course/assessment/question/text_responses/edit.html.slim b/app/views/course/assessment/question/text_responses/edit.html.slim index 9d86c9e24b2..a777c8100dc 100644 --- a/app/views/course/assessment/question/text_responses/edit.html.slim +++ b/app/views/course/assessment/question/text_responses/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb format_inline_text(@question_assessment.display_title) div#app-root diff --git a/app/views/course/assessment/question/text_responses/new.html.slim b/app/views/course/assessment/question/text_responses/new.html.slim index e8369f6d9db..a777c8100dc 100644 --- a/app/views/course/assessment/question/text_responses/new.html.slim +++ b/app/views/course/assessment/question/text_responses/new.html.slim @@ -1,7 +1 @@ -- if @text_response_question.file_upload_question? - - add_breadcrumb t('.file_upload') -- elsif @text_response_question.comprehension_question? - - add_breadcrumb t('.comprehension') -- else - - add_breadcrumb t('.text_response') div#app-root diff --git a/app/views/course/assessment/question/voice_responses/edit.html.slim b/app/views/course/assessment/question/voice_responses/edit.html.slim index 9d86c9e24b2..a777c8100dc 100644 --- a/app/views/course/assessment/question/voice_responses/edit.html.slim +++ b/app/views/course/assessment/question/voice_responses/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb format_inline_text(@question_assessment.display_title) div#app-root diff --git a/app/views/course/assessment/question/voice_responses/new.html.slim b/app/views/course/assessment/question/voice_responses/new.html.slim index 23ca4c33d71..a777c8100dc 100644 --- a/app/views/course/assessment/question/voice_responses/new.html.slim +++ b/app/views/course/assessment/question/voice_responses/new.html.slim @@ -1,2 +1 @@ -- add_breadcrumb t('.voice_response_header') div#app-root diff --git a/app/views/course/assessment/question_bundle_assignments/edit.html.slim b/app/views/course/assessment/question_bundle_assignments/edit.html.slim index 67c4b028f10..1664dfc9a42 100644 --- a/app/views/course/assessment/question_bundle_assignments/edit.html.slim +++ b/app/views/course/assessment/question_bundle_assignments/edit.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb format_inline_text(@question_bundle_assignment.user.name) = page_header 'Edit Question Bundle Assignment' - url = course_assessment_question_bundle_assignment_path(current_course, @assessment, @question_bundle_assignment) diff --git a/app/views/course/assessment/question_bundle_questions/edit.html.slim b/app/views/course/assessment/question_bundle_questions/edit.html.slim index bbce538b39d..df420915434 100644 --- a/app/views/course/assessment/question_bundle_questions/edit.html.slim +++ b/app/views/course/assessment/question_bundle_questions/edit.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb format_inline_text(@question_bundle_question.question.title) = page_header 'Edit Question Bundle Question' = simple_form_for @question_bundle_question, diff --git a/app/views/course/assessment/question_bundle_questions/new.html.slim b/app/views/course/assessment/question_bundle_questions/new.html.slim index bd11ce6d097..690f550fc64 100644 --- a/app/views/course/assessment/question_bundle_questions/new.html.slim +++ b/app/views/course/assessment/question_bundle_questions/new.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb 'New Question Bundle Question' = page_header 'New Question Bundle Question' = simple_form_for @question_bundle_question, url: course_assessment_question_bundle_questions_path(current_course, @assessment) do |f| diff --git a/app/views/course/assessment/question_bundles/edit.html.slim b/app/views/course/assessment/question_bundles/edit.html.slim index 548c0d6cd5c..0be90c31b66 100644 --- a/app/views/course/assessment/question_bundles/edit.html.slim +++ b/app/views/course/assessment/question_bundles/edit.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb format_inline_text(@question_bundle.title) = page_header 'Edit Question Bundle' = simple_form_for @question_bundle, diff --git a/app/views/course/assessment/question_bundles/new.html.slim b/app/views/course/assessment/question_bundles/new.html.slim index 1696b3f9194..f9928623c0f 100644 --- a/app/views/course/assessment/question_bundles/new.html.slim +++ b/app/views/course/assessment/question_bundles/new.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb 'New Question Bundle' = page_header 'New Question Bundle' = simple_form_for @question_bundle, url: course_assessment_question_bundles_path(current_course, @assessment) do |f| diff --git a/app/views/course/assessment/question_groups/edit.html.slim b/app/views/course/assessment/question_groups/edit.html.slim index 2ad374292fd..b354f188834 100644 --- a/app/views/course/assessment/question_groups/edit.html.slim +++ b/app/views/course/assessment/question_groups/edit.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb format_inline_text(@question_group.title) = page_header 'Edit Question Group' = simple_form_for @question_group, diff --git a/app/views/course/assessment/question_groups/new.html.slim b/app/views/course/assessment/question_groups/new.html.slim index 363fa0a1d85..57e486fb60b 100644 --- a/app/views/course/assessment/question_groups/new.html.slim +++ b/app/views/course/assessment/question_groups/new.html.slim @@ -1,4 +1,3 @@ -- add_breadcrumb 'New Question Group' = page_header 'New Question Group' = simple_form_for @question_group, url: course_assessment_question_groups_path(current_course, @assessment) do |f| diff --git a/app/views/course/assessment/submission/logs/index.html.slim b/app/views/course/assessment/submission/logs/index.html.slim index 8a77a8226af..a777c8100dc 100644 --- a/app/views/course/assessment/submission/logs/index.html.slim +++ b/app/views/course/assessment/submission/logs/index.html.slim @@ -1,3 +1 @@ -- add_breadcrumb t('.submissions') -- add_breadcrumb t('.header') div#app-root diff --git a/app/views/course/assessment/submission/submissions/edit.html.slim b/app/views/course/assessment/submission/submissions/edit.html.slim index 24550ecd8c8..a777c8100dc 100644 --- a/app/views/course/assessment/submission/submissions/edit.html.slim +++ b/app/views/course/assessment/submission/submissions/edit.html.slim @@ -1,2 +1 @@ -- add_breadcrumb t('.attempt') div#app-root diff --git a/app/views/course/assessment/submission/submissions/index.html.slim b/app/views/course/assessment/submission/submissions/index.html.slim index b9e2d6f6f11..a777c8100dc 100644 --- a/app/views/course/assessment/submission/submissions/index.html.slim +++ b/app/views/course/assessment/submission/submissions/index.html.slim @@ -1,2 +1 @@ -- add_breadcrumb t('.submissions') div#app-root diff --git a/app/views/course/courses/_course_data.json.jbuilder b/app/views/course/courses/_course_data.json.jbuilder index 3056778d368..5083d840089 100644 --- a/app/views/course/courses/_course_data.json.jbuilder +++ b/app/views/course/courses/_course_data.json.jbuilder @@ -19,7 +19,7 @@ if can?(:manage, current_course) || current_course.user?(current_user) # Announcements if @currently_active_announcements && !@currently_active_announcements.empty? json.currentlyActiveAnnouncements @currently_active_announcements do |announcement| - json.partial! 'announcements/announcement_list_data', announcement: announcement + json.partial! 'announcements/announcement_data', announcement: announcement end else json.currentlyActiveAnnouncements nil diff --git a/app/views/course/courses/_course_list_data.json.jbuilder b/app/views/course/courses/_course_list_data.json.jbuilder index 1759132aca5..064096f615a 100644 --- a/app/views/course/courses/_course_list_data.json.jbuilder +++ b/app/views/course/courses/_course_list_data.json.jbuilder @@ -3,5 +3,5 @@ json.id course.id json.title course.title json.description format_ckeditor_rich_text(course.description) -json.logoUrl display_course_logo(course) +json.logoUrl url_to_course_logo(course) json.startAt course.start_at diff --git a/app/views/course/courses/_course_user_progress.json.jbuilder b/app/views/course/courses/_course_user_progress.json.jbuilder new file mode 100644 index 00000000000..a0731445411 --- /dev/null +++ b/app/views/course/courses/_course_user_progress.json.jbuilder @@ -0,0 +1,27 @@ +# frozen_string_literal: true +levels_enabled = !current_component_host[:course_levels_component].nil? +achievements_enabled = !current_component_host[:course_achievements_component].nil? + +if levels_enabled + json.level course_user.level_number + json.nextLevelPercentage course_user.level_progress_percentage + + experience_points = course_user.experience_points + json.exp experience_points + + next_threshold = course_user.next_level_threshold + difference = next_threshold - experience_points + json.nextLevelExpDelta difference > 0 ? difference : 'max' +end + +if achievements_enabled + recent_achievements = course_user.achievements.recently_obtained(5) + + json.recentAchievements do + json.partial! 'course/assessment/assessments/achievement_badges', + achievements: recent_achievements, + course: course_user.course + end + + json.remainingAchievementsCount course_user.achievement_count - recent_achievements.size +end diff --git a/app/views/course/courses/_sidebar_items.json.jbuilder b/app/views/course/courses/_sidebar_items.json.jbuilder new file mode 100644 index 00000000000..61530207369 --- /dev/null +++ b/app/views/course/courses/_sidebar_items.json.jbuilder @@ -0,0 +1,8 @@ +# frozen_string_literal: true +json.array! items do |item| + json.key item[:key] + json.label item[:title] + json.path item[:path] + json.icon item[:icon] + json.unread item[:unread] if item[:unread]&.nonzero? +end diff --git a/app/views/course/courses/sidebar.json.jbuilder b/app/views/course/courses/sidebar.json.jbuilder new file mode 100644 index 00000000000..c20100fc7ff --- /dev/null +++ b/app/views/course/courses/sidebar.json.jbuilder @@ -0,0 +1,34 @@ +# frozen_string_literal: true +json.courseTitle current_course.title +json.courseUrl course_path(current_course) +json.courseLogoUrl url_to_course_logo(current_course) +json.courseUserUrl url_to_user_or_course_user(current_course, current_course_user) +json.userName current_user.name + +if current_course_user.present? && can?(:read, current_course) + json.courseUserName current_course_user.name + json.courseUserRole current_course_user.role + json.userAvatarUrl user_image(current_course_user.user) + + if can?(:manage, Course::UserEmailUnsubscription.new(course_user: current_course_user)) + json.manageEmailSubscriptionUrl course_user_manage_email_subscription_path(current_course, current_course_user) + end + + if current_course_user.student? && current_course.gamified? + json.progress do + json.partial! 'course_user_progress', course_user: current_course_user + end + end +end + +if can?(:read, current_course) + json.sidebar do + json.partial! 'sidebar_items', items: controller.sidebar_items(type: :normal) + end + + unless (admin_sidebar_items = controller.sidebar_items(type: :admin)).empty? + json.adminSidebar do + json.partial! 'sidebar_items', items: admin_sidebar_items + end + end +end diff --git a/app/views/course/duplications/show.html.slim b/app/views/course/duplications/show.html.slim index 864b8057d21..a777c8100dc 100644 --- a/app/views/course/duplications/show.html.slim +++ b/app/views/course/duplications/show.html.slim @@ -1,3 +1 @@ -- add_breadcrumb :show - div#app-root diff --git a/app/views/course/enrol_requests/index.html.slim b/app/views/course/enrol_requests/index.html.slim index 3e304bda465..a777c8100dc 100644 --- a/app/views/course/enrol_requests/index.html.slim +++ b/app/views/course/enrol_requests/index.html.slim @@ -1,3 +1 @@ -- add_breadcrumb t('.header') - div#app-root diff --git a/app/views/course/personal_times/index.html.slim b/app/views/course/personal_times/index.html.slim index bb12b31a621..a777c8100dc 100644 --- a/app/views/course/personal_times/index.html.slim +++ b/app/views/course/personal_times/index.html.slim @@ -1,3 +1 @@ -- add_breadcrumb :index - div#app-root diff --git a/app/views/course/reference_timelines/index.html.slim b/app/views/course/reference_timelines/index.html.slim index 31712dfc9ce..a777c8100dc 100644 --- a/app/views/course/reference_timelines/index.html.slim +++ b/app/views/course/reference_timelines/index.html.slim @@ -1,2 +1 @@ -- add_breadcrumb I18n.t('layouts.multiple_reference_timelines.timeline_designer') -div#app-root.dangerously-fit-height-to-viewport +div#app-root diff --git a/app/views/course/user_invitations/index.html.slim b/app/views/course/user_invitations/index.html.slim index bb12b31a621..a777c8100dc 100644 --- a/app/views/course/user_invitations/index.html.slim +++ b/app/views/course/user_invitations/index.html.slim @@ -1,3 +1 @@ -- add_breadcrumb :index - div#app-root diff --git a/app/views/course/user_invitations/new.html.slim b/app/views/course/user_invitations/new.html.slim index 48c1f09dfab..a777c8100dc 100644 --- a/app/views/course/user_invitations/new.html.slim +++ b/app/views/course/user_invitations/new.html.slim @@ -1,3 +1 @@ -- add_breadcrumb :invite, :course_users_students_path - div#app-root diff --git a/app/views/course/users/index.html.slim b/app/views/course/users/index.html.slim index bdb6d2d746b..a777c8100dc 100644 --- a/app/views/course/users/index.html.slim +++ b/app/views/course/users/index.html.slim @@ -1,2 +1 @@ -- add_breadcrumb t('.header') div#app-root diff --git a/app/views/course/users/staff.html.slim b/app/views/course/users/staff.html.slim index 2f348baa970..a777c8100dc 100644 --- a/app/views/course/users/staff.html.slim +++ b/app/views/course/users/staff.html.slim @@ -1,3 +1 @@ -- add_breadcrumb :staff - div#app-root diff --git a/app/views/course/users/students.html.slim b/app/views/course/users/students.html.slim index 91bf5a6ebc1..a777c8100dc 100644 --- a/app/views/course/users/students.html.slim +++ b/app/views/course/users/students.html.slim @@ -1,3 +1 @@ -- add_breadcrumb :students - div#app-root diff --git a/app/views/layouts/_course_user_badge.html.slim b/app/views/layouts/_course_user_badge.html.slim deleted file mode 100644 index d5daab5abfb..00000000000 --- a/app/views/layouts/_course_user_badge.html.slim +++ /dev/null @@ -1,35 +0,0 @@ -- levels_enabled = !current_component_host[:course_levels_component].nil? -- achievements_enabled = !current_component_host[:course_achievements_component].nil? - -- if levels_enabled - - level_number = course_user.level_number - - percentage = course_user.level_progress_percentage - - experience_points = course_user.experience_points - - next_threshold = course_user.next_level_threshold - - difference = next_threshold - experience_points - - div.col-xs-12#course-badge-level - = link_to_course_user(course_user) do - span.level = t('.level', level: level_number) - span.experience-points = t('.experience_points', exp: experience_points) - - - progress_bar_options = { class: ['progress-bar-info', 'course-user-experience-points'], - tooltip_text: t('common.percentage', percentage: percentage), - tooltip_placement: 'right' } - = display_progress_bar(percentage, progress_bar_options) - div.next-level.text-center - - if difference > 0 - = t('.next_level', difference: difference) - - else - = t('.max_level') - -- if achievements_enabled - div.col-xs-12#course-badge-achievement - - course_user.achievements.recently_obtained(3).each do |achievement| - = content_tag_for(:span, achievement) do - = link_to course_achievement_path(current_course, achievement) do - = display_achievement_badge(achievement) - - if course_user.achievement_count > 3 - - difference = course_user.achievement_count - 3 - = link_to t('.achievements', difference: difference), - course_achievements_path(current_course), class: ['achievement-difference'] diff --git a/app/views/layouts/_sidebar.html.slim b/app/views/layouts/_sidebar.html.slim deleted file mode 100644 index 3046ffd6500..00000000000 --- a/app/views/layouts/_sidebar.html.slim +++ /dev/null @@ -1,4 +0,0 @@ -div class=sidebar_classes - nav.navbar-default role='navigation' - div.sidebar.collapse.navbar-collapse#site-navigation-sidebar - = yield diff --git a/app/views/layouts/course.html.slim b/app/views/layouts/course.html.slim index 0dc7c80d37e..db67704c57f 100644 --- a/app/views/layouts/course.html.slim +++ b/app/views/layouts/course.html.slim @@ -1,38 +1,3 @@ - layout = controller.parent_layout(of_layout: 'course') || controller.current_layout = render within_layout: layout do - = render_breadcrumbs - div.course-layout data-course-id=current_course.id - button.btn.btn-default.collapse#show-sidebar type="button" data-toggle="collapse" data-target="#full-sidebar,#show-sidebar" aria-expanded="false" aria-controls="navbar" - = fa_icon 'chevron-right'.freeze - aside.collapse.in#full-sidebar - div.row - div.hidden-xs.text-center#course-sidebar-logo - button.btn.btn-default.pull-left#hide-sidebar type="button" data-toggle="collapse" data-target="#full-sidebar,#show-sidebar" aria-expanded="false" aria-controls="navbar" - = fa_icon 'chevron-left'.freeze - = link_to course_path(current_course) do - = display_course_logo(current_course) - - if current_course_user.present? && can?(:read, current_course) - div.user - = display_user_image(current_user) - span#user-sidebar - span#user-link-sidebar = link_to_user(current_course_user || current_user) - - if current_course_user && can?(:manage, Course::UserEmailUnsubscription.new(course_user: current_course_user)) - span#manage-email-subscription-link-sidebar - = link_to course_user_manage_email_subscription_path(current_course, current_course_user) do - = t('.manage_subscriptions') - = fa_icon 'envelope'.freeze - button.btn.btn-default.navbar-toggle.collapsed.pull-right type="button" data-toggle="collapse" data-target="#course-navigation-sidebar" aria-expanded="false" aria-controls="navbar" - span.glyphicon.glyphicon-menu-hamburger - - if current_course_user.student? && current_course.gamified? - = display_course_user_badge(current_course_user) - nav.navbar-default role='navigation' - - if can?(:read, current_course) - div.sidebar.collapse.navbar-collapse#course-navigation-sidebar - = sidebar_items(controller.sidebar_items(type: :normal)) - - - unless (admin_sidebar_items = controller.sidebar_items(type: :admin)).empty? - h4#admin-header-sidebar = t('.administration') - = sidebar_items(admin_sidebar_items) - - div.page-content class=page_class - = yield + div#app-root diff --git a/app/views/layouts/course_admin.html.slim b/app/views/layouts/course_admin.html.slim deleted file mode 100644 index ee6857437c9..00000000000 --- a/app/views/layouts/course_admin.html.slim +++ /dev/null @@ -1,7 +0,0 @@ -= render within_layout: controller.parent_layout(of_layout: 'course_admin') do - = page_header t('.title') - = tabs do - - controller.sidebar_items(type: :settings).each do |item| - = nav_to(item[:title], item[:path]) - div class=page_class - = yield diff --git a/app/views/pages/home.json.jbuilder b/app/views/pages/home.json.jbuilder new file mode 100644 index 00000000000..48ba8fcf553 --- /dev/null +++ b/app/views/pages/home.json.jbuilder @@ -0,0 +1,25 @@ +# frozen_string_literal: true +if user_signed_in? + my_courses = Course.containing_user(current_user).ordered_by_start_at + if my_courses.present? + json.courses my_courses do |course| + json.title course.title + json.url course_path(course) + end + end + + json.user do + json.name current_user.name + json.url user_path(current_user) + json.avatarUrl user_image(current_user) + json.role current_user.role + json.instanceRole controller.current_instance_user.role + end + + json.signOutUrl destroy_user_session_path + + if user_masquerade? + json.masqueradeUserName current_user.name + json.stopMasqueradingUrl back_masquerade_path(current_user) + end +end diff --git a/app/views/system/admin/announcements/index.json.jbuilder b/app/views/system/admin/announcements/index.json.jbuilder index 3f0fc92f0a8..e946b6edf9b 100644 --- a/app/views/system/admin/announcements/index.json.jbuilder +++ b/app/views/system/admin/announcements/index.json.jbuilder @@ -1,7 +1,7 @@ # frozen_string_literal: true json.announcements @announcements do |announcement| - json.partial! 'announcements/announcement_list_data', announcement: announcement + json.partial! 'announcements/announcement_data', announcement: announcement end json.permissions do diff --git a/app/views/system/admin/instance/announcements/index.json.jbuilder b/app/views/system/admin/instance/announcements/index.json.jbuilder index 08ce29813c7..f6d2298a867 100644 --- a/app/views/system/admin/instance/announcements/index.json.jbuilder +++ b/app/views/system/admin/instance/announcements/index.json.jbuilder @@ -1,6 +1,6 @@ # frozen_string_literal: true json.announcements @announcements do |announcement| - json.partial! 'announcements/announcement_list_data', announcement: announcement + json.partial! 'announcements/announcement_data', announcement: announcement end json.permissions do diff --git a/client/.eslintrc.js b/client/.eslintrc.js index cb256ea0b3e..36bd671b604 100644 --- a/client/.eslintrc.js +++ b/client/.eslintrc.js @@ -110,7 +110,7 @@ module.exports = { }, ], 'import/no-extraneous-dependencies': ['warn', { devDependencies: true }], - 'import/prefer-default-export': 'warn', + 'import/prefer-default-export': 'off', 'jsx-a11y/anchor-is-valid': 'off', 'jsx-a11y/click-events-have-key-events': 'off', 'jsx-a11y/label-has-for': 'off', @@ -170,6 +170,8 @@ module.exports = { File: true, FileReader: true, JSX: true, + FIRST_BUILD_YEAR: true, + LATEST_BUILD_YEAR: true, }, overrides: [ { diff --git a/client/app/App.tsx b/client/app/App.tsx index 606f8de8bc2..4539ec6e7ab 100644 --- a/client/app/App.tsx +++ b/client/app/App.tsx @@ -1,19 +1,14 @@ -import { BrowserRouter } from 'react-router-dom'; +import { createBrowserRouter, RouterProvider } from 'react-router-dom'; -import ProviderWrapper from 'lib/components/wrappers/ProviderWrapper'; -import NotificationPopup from 'lib/containers/NotificationPopup'; +import Providers from 'lib/components/wrappers/Providers'; -import RoutedApp from './RoutedApp'; +import router from './router'; import { store } from './store'; const App = (): JSX.Element => ( - - - - - - - + + + ); export default App; diff --git a/client/app/RoutedApp.tsx b/client/app/RoutedApp.tsx deleted file mode 100644 index 3ac11f515db..00000000000 --- a/client/app/RoutedApp.tsx +++ /dev/null @@ -1,337 +0,0 @@ -import { Navigate, Route, Routes } from 'react-router-dom'; - -import GlobalAnnouncementIndex from 'bundles/announcements/GlobalAnnouncementIndex'; -import AchievementShow from 'bundles/course/achievement/pages/AchievementShow'; -import AchievementsIndex from 'bundles/course/achievement/pages/AchievementsIndex'; -import SettingsNavigation from 'bundles/course/admin/components/SettingsNavigation'; -import AnnouncementSettings from 'bundles/course/admin/pages/AnnouncementsSettings'; -import AssessmentSettings from 'bundles/course/admin/pages/AssessmentSettings'; -import CodaveriSettings from 'bundles/course/admin/pages/CodaveriSettings'; -import CommentsSettings from 'bundles/course/admin/pages/CommentsSettings'; -import ComponentSettings from 'bundles/course/admin/pages/ComponentSettings'; -import CourseSettings from 'bundles/course/admin/pages/CourseSettings'; -import ForumsSettings from 'bundles/course/admin/pages/ForumsSettings'; -import LeaderboardSettings from 'bundles/course/admin/pages/LeaderboardSettings'; -import LessonPlanSettings from 'bundles/course/admin/pages/LessonPlanSettings'; -import MaterialsSettings from 'bundles/course/admin/pages/MaterialsSettings'; -import NotificationSettings from 'bundles/course/admin/pages/NotificationSettings'; -import SidebarSettings from 'bundles/course/admin/pages/SidebarSettings'; -import VideosSettings from 'bundles/course/admin/pages/VideosSettings'; -import AnnouncementsIndex from 'bundles/course/announcements/pages/AnnouncementsIndex'; -import AssessmentEdit from 'bundles/course/assessment/pages/AssessmentEdit'; -import AssessmentMonitoring from 'bundles/course/assessment/pages/AssessmentMonitoring'; -import AssessmentShow from 'bundles/course/assessment/pages/AssessmentShow'; -import AssessmentsIndex from 'bundles/course/assessment/pages/AssessmentsIndex'; -import AssessmentStatisticsPage from 'bundles/course/assessment/pages/AssessmentStatistics'; -import EditForumPostResponsePage from 'bundles/course/assessment/question/forum-post-responses/EditForumPostResponsePage'; -import NewForumPostResponsePage from 'bundles/course/assessment/question/forum-post-responses/NewForumPostResponsePage'; -import EditMcqMrqPage from 'bundles/course/assessment/question/multiple-responses/EditMcqMrqPage'; -import NewMcqMrqPage from 'bundles/course/assessment/question/multiple-responses/NewMcqMrqPage'; -import EditProgrammingQuestionPage from 'bundles/course/assessment/question/programming/EditProgrammingQuestionPage'; -import NewProgrammingQuestionPage from 'bundles/course/assessment/question/programming/NewProgrammingQuestionPage'; -import ScribingQuestion from 'bundles/course/assessment/question/scribing/ScribingQuestion'; -import EditTextResponse from 'bundles/course/assessment/question/text-responses/EditTextResponsePage'; -import NewTextResponse from 'bundles/course/assessment/question/text-responses/NewTextResponsePage'; -import EditVoicePage from 'bundles/course/assessment/question/voice-responses/EditVoicePage'; -import NewVoicePage from 'bundles/course/assessment/question/voice-responses/NewVoicePage'; -import AssessmentSessionNew from 'bundles/course/assessment/sessions/pages/AssessmentSessionNew'; -import SkillsIndex from 'bundles/course/assessment/skills/pages/SkillsIndex'; -import LogsIndex from 'bundles/course/assessment/submission/pages/LogsIndex'; -import SubmissionEditIndex from 'bundles/course/assessment/submission/pages/SubmissionEditIndex'; -import AssessmentSubmissionsIndex from 'bundles/course/assessment/submission/pages/SubmissionsIndex'; -import SubmissionsIndex from 'bundles/course/assessment/submissions/SubmissionsIndex'; -import CourseShow from 'bundles/course/courses/pages/CourseShow'; -import CoursesIndex from 'bundles/course/courses/pages/CoursesIndex'; -import CommentIndex from 'bundles/course/discussion/topics/pages/CommentIndex'; -import Duplication from 'bundles/course/duplication/pages/Duplication'; -import UserRequests from 'bundles/course/enrol-requests/pages/UserRequests'; -import DisbursementIndex from 'bundles/course/experience-points/disbursement/pages/DisbursementIndex'; -import ForumShow from 'bundles/course/forum/pages/ForumShow'; -import ForumsIndex from 'bundles/course/forum/pages/ForumsIndex'; -import ForumTopicShow from 'bundles/course/forum/pages/ForumTopicShow'; -import GroupIndex from 'bundles/course/group/pages/GroupIndex'; -import GroupShow from 'bundles/course/group/pages/GroupShow'; -import LeaderboardIndex from 'bundles/course/leaderboard/pages/LeaderboardIndex'; -import LearningMap from 'bundles/course/learning-map/containers/LearningMap'; -import LessonPlanLayout from 'bundles/course/lesson-plan/containers/LessonPlanLayout'; -import LessonPlanEdit from 'bundles/course/lesson-plan/pages/LessonPlanEdit'; -import LessonPlanShow from 'bundles/course/lesson-plan/pages/LessonPlanShow'; -import LevelsIndex from 'bundles/course/level/pages/LevelsIndex'; -import FolderShow from 'bundles/course/material/folders/pages/FolderShow'; -import TimelineDesigner from 'bundles/course/reference-timelines/TimelineDesigner'; -import StatisticsIndex from 'bundles/course/statistics/pages/StatisticsIndex'; -import ResponseEdit from 'bundles/course/survey/pages/ResponseEdit'; -import ResponseIndex from 'bundles/course/survey/pages/ResponseIndex'; -import ResponseShow from 'bundles/course/survey/pages/ResponseShow'; -import SurveyIndex from 'bundles/course/survey/pages/SurveyIndex'; -import SurveyResults from 'bundles/course/survey/pages/SurveyResults'; -import SurveyShow from 'bundles/course/survey/pages/SurveyShow'; -import UserEmailSubscriptions from 'bundles/course/user-email-subscriptions/UserEmailSubscriptions'; -import InvitationsIndex from 'bundles/course/user-invitations/pages/InvitationsIndex'; -import InviteUsers from 'bundles/course/user-invitations/pages/InviteUsers'; -import ExperiencePointsRecords from 'bundles/course/users/pages/ExperiencePointsRecords'; -import ManageStaff from 'bundles/course/users/pages/ManageStaff'; -import ManageStudents from 'bundles/course/users/pages/ManageStudents'; -import PersonalTimes from 'bundles/course/users/pages/PersonalTimes'; -import PersonalTimesShow from 'bundles/course/users/pages/PersonalTimesShow'; -import CourseUserShow from 'bundles/course/users/pages/UserShow'; -import UsersIndex from 'bundles/course/users/pages/UsersIndex'; -import VideoShow from 'bundles/course/video/pages/VideoShow'; -import VideosIndex from 'bundles/course/video/pages/VideosIndex'; -import VideoSubmissionEdit from 'bundles/course/video/submission/pages/VideoSubmissionEdit'; -import VideoSubmissionShow from 'bundles/course/video/submission/pages/VideoSubmissionShow'; -import VideoSubmissionsIndex from 'bundles/course/video/submission/pages/VideoSubmissionsIndex'; -import UserVideoSubmissionsIndex from 'bundles/course/video-submissions/pages/UserVideoSubmissionsIndex'; -import AdminIndex from 'bundles/system/admin/admin/pages/AdminIndex'; -import AdminNavigator from 'bundles/system/admin/admin/pages/AdminNavigator'; -import AnnouncementIndex from 'bundles/system/admin/admin/pages/AnnouncementsIndex'; -import CourseIndex from 'bundles/system/admin/admin/pages/CoursesIndex'; -import InstancesIndex from 'bundles/system/admin/admin/pages/InstancesIndex'; -import UserIndex from 'bundles/system/admin/admin/pages/UsersIndex'; -import InstanceAdminIndex from 'bundles/system/admin/instance/instance/pages/InstanceAdminIndex'; -import InstanceAdminNavigator from 'bundles/system/admin/instance/instance/pages/InstanceAdminNavigator'; -import InstanceAnnouncementsIndex from 'bundles/system/admin/instance/instance/pages/InstanceAnnouncementsIndex'; -import InstanceComponentsIndex from 'bundles/system/admin/instance/instance/pages/InstanceComponentsIndex'; -import InstanceCoursesIndex from 'bundles/system/admin/instance/instance/pages/InstanceCoursesIndex'; -import InstanceUserRoleRequestsIndex from 'bundles/system/admin/instance/instance/pages/InstanceUserRoleRequestsIndex'; -import InstanceUsersIndex from 'bundles/system/admin/instance/instance/pages/InstanceUsersIndex'; -import InstanceUsersInvitations from 'bundles/system/admin/instance/instance/pages/InstanceUsersInvitations'; -import InstanceUsersInvite from 'bundles/system/admin/instance/instance/pages/InstanceUsersInvite'; -import AccountSettings from 'bundles/user/AccountSettings'; -import UserShow from 'bundles/users/pages/UserShow'; -import PopupNotifierOutlet from 'course/user-notification/PopupNotifierOutlet'; - -const RoutedApp = (): JSX.Element => ( - - - } index /> - - } path=":courseId"> - } index /> - - - } index /> - } path=":achievementId" /> - - - } path="announcements" /> - - - } index /> - } path="submissions" /> - } path="skills" /> - - - } index /> - } path="edit" /> - } path="monitoring" /> - } path="sessions/new" /> - - {/* @ts-ignore `connect` throws error when cannot find `store` as direct parent */} - } path="statistics" /> - - - } index /> - - - } path="edit" /> - } path="logs" /> - - - - - - } path="new" /> - - } - path=":questionId/edit" - /> - - - - } path="new" /> - } path=":questionId/edit" /> - - - - } path="new" /> - - } - path=":questionId/edit" - /> - - - - } path="new" /> - } path=":questionId/edit" /> - - - - } path="new" /> - } path=":questionId/edit" /> - - - - } path="new" /> - } path=":questionId/edit" /> - - - - - - } path="comments" /> - } path="duplication" /> - } path="enrol_requests" /> - - - } index /> - - - } index /> - } path="topics/:topicId" /> - - - - } path="groups"> - } path=":groupCategoryId" /> - - - {/* @ts-ignore `connect` throws error when cannot find `store` as direct parent */} - } path="lesson_plan"> - } index /> - } path="edit" /> - - - } path="leaderboard" /> - } path="learning_map" /> - } path="levels" /> - } path="materials/folders/:folderId" /> - } path="statistics" /> - } path="timelines" /> - - - } index /> - } path="personal_times" /> - } path="invite" /> - - } - path="disburse_experience_points" - /> - - - } index /> - } path="personal_times" /> - - } - path="video_submissions" - /> - - } - path="experience_points_records" - /> - - } - path="manage_email_subscription" - /> - - - } path="user_invitations" /> - } path="students" /> - } path="staff" /> - - - } index /> - - - } index /> - - - } index /> - - - } index /> - } path="edit" /> - - - - - - - } index /> - - - } index /> - } path="results" /> - - - } index /> - - - } index /> - } path="edit" /> - - - - - - } path="admin"> - } index /> - } path="components" /> - } path="sidebar" /> - } path="notifications" /> - } path="announcements" /> - } path="assessments" /> - } path="materials" /> - } path="forums" /> - } path="leaderboard" /> - } path="comments" /> - } path="videos" /> - } path="lesson_plan" /> - } path="codaveri" /> - - - - - } path="announcements" /> - - } path="users/:userId" /> - } path="user/profile/edit" /> - - } path="admin"> - } index /> - } path="announcements" /> - } path="users" /> - } path="instances" /> - } path="courses" /> - - - {/* `admin/instance` cannot be nested in `admin/*` because it has a different container element */} - } path="admin/instance"> - } index /> - } path="announcements" /> - } path="components" /> - } path="courses" /> - } path="users" /> - } path="users/invite" /> - } path="user_invitations" /> - } path="role_requests" /> - - - {/* `/role_requests` will be routed without `InstanceAdminNavigator` and its sidebar. - Here, we redirect `/role_requests` to `/admin/instance/role_requests`. But until - we are loading the page without Rails' router, fresh loads to `/role_requests` will - go to 404. Once we are 100% SPA, this is a non-issue. */} - } - path="role_requests" - /> - -); - -export default RoutedApp; diff --git a/client/app/__test__/setup.js b/client/app/__test__/setup.js index f36c68ad55d..641d603e766 100644 --- a/client/app/__test__/setup.js +++ b/client/app/__test__/setup.js @@ -72,4 +72,5 @@ jest.mock('lib/components/form/fields/RichTextField', () => jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn(), + unstable_usePrompt: jest.fn(), })); diff --git a/client/app/api/Announcements.ts b/client/app/api/Announcements.ts index 02b26b5e66b..931929d575b 100644 --- a/client/app/api/Announcements.ts +++ b/client/app/api/Announcements.ts @@ -1,4 +1,4 @@ -import { AnnouncementListData } from 'types/course/announcements'; +import { AnnouncementData } from 'types/course/announcements'; import BaseAPI from './Base'; import { APIResponse } from './types'; @@ -12,9 +12,13 @@ export default class AnnouncementsAPI extends BaseAPI { /** * Fetches all the announcements (admin and instance announcements) */ - index(): APIResponse<{ - announcements: AnnouncementListData[]; + index(unread = false): APIResponse<{ + announcements: AnnouncementData[]; }> { - return this.client.get(this.#urlPrefix); + return this.client.get(this.#urlPrefix, { params: { unread } }); + } + + markAsRead(url: string): APIResponse { + return this.client.post(url); } } diff --git a/client/app/api/Home.ts b/client/app/api/Home.ts new file mode 100644 index 00000000000..68a687491c6 --- /dev/null +++ b/client/app/api/Home.ts @@ -0,0 +1,15 @@ +import { HomeLayoutData } from 'types/home'; + +import BaseAPI from './Base'; +import { APIResponse } from './types'; + +export default class HomeAPI extends BaseAPI { + // eslint-disable-next-line class-methods-use-this + get #urlPrefix(): string { + return '/'; + } + + fetch(): APIResponse { + return this.client.get(this.#urlPrefix); + } +} diff --git a/client/app/api/Users.ts b/client/app/api/Users.ts index 05c5fbc9814..c12ee2c36a8 100644 --- a/client/app/api/Users.ts +++ b/client/app/api/Users.ts @@ -77,4 +77,8 @@ export default class UsersAPI extends BaseAPI { ): APIResponse { return this.client.post(url); } + + signOut(url: string): APIResponse { + return this.client.delete(url); + } } diff --git a/client/app/api/course/Announcements.ts b/client/app/api/course/Announcements.ts index 74f2210647f..61c6d14c961 100644 --- a/client/app/api/course/Announcements.ts +++ b/client/app/api/course/Announcements.ts @@ -1,10 +1,10 @@ -import { AxiosResponse } from 'axios'; import { AnnouncementData, - AnnouncementListData, - AnnouncementPermissions, + FetchAnnouncementsData, } from 'types/course/announcements'; +import { APIResponse } from 'api/types'; + import BaseCourseAPI from './Base'; export default class AnnouncementsAPI extends BaseCourseAPI { @@ -15,19 +15,14 @@ export default class AnnouncementsAPI extends BaseCourseAPI { /** * Fetches all the announcements */ - index(): Promise< - AxiosResponse<{ - announcements: AnnouncementListData[]; - permissions: AnnouncementPermissions; - }> - > { + index(): APIResponse { return this.client.get(this.#urlPrefix); } /** * Creates a new announcement */ - create(params: FormData): Promise> { + create(params: FormData): APIResponse { return this.client.post(this.#urlPrefix, params); } @@ -37,7 +32,7 @@ export default class AnnouncementsAPI extends BaseCourseAPI { update( announcementId: number, params: FormData | object, - ): Promise> { + ): APIResponse { return this.client.patch(`${this.#urlPrefix}/${announcementId}`, params); } @@ -49,7 +44,7 @@ export default class AnnouncementsAPI extends BaseCourseAPI { * success response: {} * error response: {} */ - delete(announcementId: number): Promise { + delete(announcementId: number): APIResponse { return this.client.delete(`${this.#urlPrefix}/${announcementId}`); } } diff --git a/client/app/api/course/Courses.ts b/client/app/api/course/Courses.ts index 5c029f1e841..f70e7128567 100644 --- a/client/app/api/course/Courses.ts +++ b/client/app/api/course/Courses.ts @@ -1,11 +1,13 @@ -import { AxiosResponse } from 'axios'; import { CourseData, + CourseLayoutData, CourseListData, CoursePermissions, } from 'types/course/courses'; import { RoleRequestBasicListData } from 'types/system/instance/roleRequests'; +import { APIResponse } from 'api/types'; + import BaseCourseAPI from './Base'; export default class CoursesAPI extends BaseCourseAPI { @@ -14,27 +16,27 @@ export default class CoursesAPI extends BaseCourseAPI { /** * Fetches all of the courses */ - index(): Promise< - AxiosResponse<{ - courses: CourseListData[]; - instanceUserRoleRequest?: RoleRequestBasicListData; - permissions: CoursePermissions; - }> - > { + index(): APIResponse<{ + courses: CourseListData[]; + instanceUserRoleRequest?: RoleRequestBasicListData; + permissions: CoursePermissions; + }> { return this.client.get(this.#baseUrlPrefix); } /** * Fetches one course */ - fetch(courseId: number): Promise< - AxiosResponse<{ - course: CourseData; - }> - > { + fetch(courseId: number): APIResponse<{ + course: CourseData; + }> { return this.client.get(`${this.#baseUrlPrefix}/${courseId}`); } + fetchLayout(courseId: number): APIResponse { + return this.client.get(`${this.#baseUrlPrefix}/${courseId}/sidebar`); + } + /** * Creates a course. * @@ -47,19 +49,17 @@ export default class CoursesAPI extends BaseCourseAPI { * error response: { errors: [] } - An array of errors will be returned upon validation error. */ - create(params: FormData): Promise< - AxiosResponse<{ - id: number; - title: string; - }> - > { + create(params: FormData): APIResponse<{ + id: number; + title: string; + }> { return this.client.post(this.#baseUrlPrefix, params); } /** * Removes a todo */ - removeTodo(ignoreLink: string): Promise { + removeTodo(ignoreLink: string): APIResponse { return this.client.post(ignoreLink); } @@ -69,7 +69,7 @@ export default class CoursesAPI extends BaseCourseAPI { sendNewRegistrationCode( registrationLink: string, myData: FormData, - ): Promise> { + ): APIResponse { return this.client.postForm(registrationLink, myData); } @@ -77,14 +77,14 @@ export default class CoursesAPI extends BaseCourseAPI { * Submits an enrol request */ - submitEnrolRequest(link: string): Promise> { + submitEnrolRequest(link: string): APIResponse<{ id: number }> { return this.client.postForm(link); } /** * Cancels a pending enrol request */ - cancelEnrolRequest(link: string): Promise> { + cancelEnrolRequest(link: string): APIResponse { return this.client.delete(link); } } diff --git a/client/app/api/index.ts b/client/app/api/index.ts index 427f44a93dd..8e67a2de0a0 100644 --- a/client/app/api/index.ts +++ b/client/app/api/index.ts @@ -1,4 +1,5 @@ import AnnouncementsAPI from './Announcements'; +import HomeAPI from './Home'; import JobsAPI from './Jobs'; import UsersAPI from './Users'; @@ -6,6 +7,7 @@ const GlobalAPI = { announcements: new AnnouncementsAPI(), jobs: new JobsAPI(), users: new UsersAPI(), + home: new HomeAPI(), }; Object.freeze(GlobalAPI); diff --git a/client/app/api/system/Admin.ts b/client/app/api/system/Admin.ts index 67095c0a7eb..76731a45d71 100644 --- a/client/app/api/system/Admin.ts +++ b/client/app/api/system/Admin.ts @@ -1,6 +1,6 @@ import { AxiosResponse } from 'axios'; import { - AnnouncementListData, + AnnouncementData, AnnouncementPermissions, } from 'types/course/announcements'; import { CourseListData } from 'types/system/courses'; @@ -27,7 +27,7 @@ export default class AdminAPI extends BaseSystemAPI { */ indexAnnouncements(): Promise< AxiosResponse<{ - announcements: AnnouncementListData[]; + announcements: AnnouncementData[]; permissions: AnnouncementPermissions; }> > { diff --git a/client/app/api/system/InstanceAdmin.ts b/client/app/api/system/InstanceAdmin.ts index 2e6875d7315..e7695c43e8c 100644 --- a/client/app/api/system/InstanceAdmin.ts +++ b/client/app/api/system/InstanceAdmin.ts @@ -1,6 +1,6 @@ import { AxiosResponse } from 'axios'; import { - AnnouncementListData, + AnnouncementData, AnnouncementPermissions, } from 'types/course/announcements'; import { CourseListData } from 'types/system/courses'; @@ -36,7 +36,7 @@ export default class InstanceAdminAPI extends BaseSystemAPI { */ indexAnnouncements(): Promise< AxiosResponse<{ - announcements: AnnouncementListData[]; + announcements: AnnouncementData[]; permissions: AnnouncementPermissions; }> > { diff --git a/client/app/bundles/announcements/GlobalAnnouncementIndex.tsx b/client/app/bundles/announcements/GlobalAnnouncementIndex.tsx index 727a527f39f..0006d00221c 100644 --- a/client/app/bundles/announcements/GlobalAnnouncementIndex.tsx +++ b/client/app/bundles/announcements/GlobalAnnouncementIndex.tsx @@ -3,8 +3,8 @@ import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; import { toast } from 'react-toastify'; import AnnouncementsDisplay from 'bundles/course/announcements/components/misc/AnnouncementsDisplay'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import { indexAnnouncements } from './operations'; @@ -45,12 +45,9 @@ const GlobalAnnouncementsIndex: FC = (props) => { /> ); - return ( - <> - - {isLoading ? : renderBody} - - ); + return {isLoading ? : renderBody}; }; -export default injectIntl(GlobalAnnouncementsIndex); +const handle = translations.header; + +export default Object.assign(injectIntl(GlobalAnnouncementsIndex), { handle }); diff --git a/client/app/bundles/announcements/operations.ts b/client/app/bundles/announcements/operations.ts index 0a253b143eb..abeb75319c8 100644 --- a/client/app/bundles/announcements/operations.ts +++ b/client/app/bundles/announcements/operations.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/prefer-default-export */ import { Operation } from 'store'; import GlobalAPI from 'api'; diff --git a/client/app/bundles/announcements/selectors.ts b/client/app/bundles/announcements/selectors.ts index 724684a2343..acc49690923 100644 --- a/client/app/bundles/announcements/selectors.ts +++ b/client/app/bundles/announcements/selectors.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/prefer-default-export */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import { AppState } from 'store'; import { selectMiniEntities } from 'utilities/store'; diff --git a/client/app/bundles/announcements/store.ts b/client/app/bundles/announcements/store.ts index ea0f6436022..1849d624ed3 100644 --- a/client/app/bundles/announcements/store.ts +++ b/client/app/bundles/announcements/store.ts @@ -1,5 +1,5 @@ import produce from 'immer'; -import { AnnouncementListData } from 'types/course/announcements'; +import { AnnouncementData } from 'types/course/announcements'; import { createEntityStore, removeAllFromStore, @@ -35,7 +35,7 @@ const reducer = produce( ); export function saveAnnouncementsList( - announcements: AnnouncementListData[], + announcements: AnnouncementData[], ): SaveAnnouncementListAction { return { type: SAVE_ANNOUNCEMENT_LIST, diff --git a/client/app/bundles/announcements/types.ts b/client/app/bundles/announcements/types.ts index f08a6afe5f5..5162cd38271 100644 --- a/client/app/bundles/announcements/types.ts +++ b/client/app/bundles/announcements/types.ts @@ -1,6 +1,6 @@ import { - AnnouncementListData, - AnnouncementMiniEntity, + AnnouncementData, + AnnouncementEntity, } from 'types/course/announcements'; import { EntityStore } from 'types/store'; @@ -10,12 +10,12 @@ export const SAVE_ANNOUNCEMENT_LIST = 'system/admin/SAVE_ANNOUNCEMENTS_LIST'; // Action Types export interface SaveAnnouncementListAction { type: typeof SAVE_ANNOUNCEMENT_LIST; - announcements: AnnouncementListData[]; + announcements: AnnouncementData[]; } export type GlobalActionType = SaveAnnouncementListAction; // State Types export interface GlobalAnnouncementState { - announcements: EntityStore; + announcements: EntityStore; } diff --git a/client/app/bundles/course/achievement/components/tables/AchievementTable.tsx b/client/app/bundles/course/achievement/components/tables/AchievementTable.tsx index 9543ce06a74..731c3c90ce7 100644 --- a/client/app/bundles/course/achievement/components/tables/AchievementTable.tsx +++ b/client/app/bundles/course/achievement/components/tables/AchievementTable.tsx @@ -1,6 +1,5 @@ import { FC, memo } from 'react'; import { defineMessages, FormattedMessage } from 'react-intl'; -import { Link } from 'react-router-dom'; import { DragIndicator } from '@mui/icons-material'; import { Switch } from '@mui/material'; import equal from 'fast-deep-equal'; @@ -11,6 +10,7 @@ import { } from 'types/course/achievements'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import Note from 'lib/components/core/Note'; import { getAchievementURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; diff --git a/client/app/bundles/course/achievement/handles.ts b/client/app/bundles/course/achievement/handles.ts new file mode 100644 index 00000000000..99718ef12e5 --- /dev/null +++ b/client/app/bundles/course/achievement/handles.ts @@ -0,0 +1,17 @@ +import { getIdFromUnknown } from 'utilities'; + +import CourseAPI from 'api/course'; +import { DataHandle } from 'lib/hooks/router/dynamicNest'; + +const getAchievementTitle = async (achievementId: number): Promise => { + const { data } = await CourseAPI.achievements.fetch(achievementId); + return data.achievement.title; +}; + +export const achievementHandle: DataHandle = (match) => { + const achievementId = getIdFromUnknown(match.params?.achievementId); + if (!achievementId) + throw new Error(`Invalid achievement id: ${achievementId}`); + + return { getData: () => getAchievementTitle(achievementId) }; +}; diff --git a/client/app/bundles/course/achievement/pages/AchievementShow/index.tsx b/client/app/bundles/course/achievement/pages/AchievementShow/index.tsx index 41aeb43aefd..1591ab3bc6e 100644 --- a/client/app/bundles/course/achievement/pages/AchievementShow/index.tsx +++ b/client/app/bundles/course/achievement/pages/AchievementShow/index.tsx @@ -1,11 +1,12 @@ -import { FC, ReactElement, useEffect, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; import { useParams } from 'react-router-dom'; import { Grid, Tooltip, Typography } from '@mui/material'; import AvatarWithLabel from 'lib/components/core/AvatarWithLabel'; +import Page from 'lib/components/core/layouts/Page'; +import Link from 'lib/components/core/Link'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { getCourseUserURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; @@ -58,27 +59,22 @@ const AchievementShow: FC = (props) => { return null; } - const headerToolbars: ReactElement[] = []; - - if (achievementMiniEntity.permissions?.canManage) { - headerToolbars.push( - , - ); - } - return ( - <> - + + ) + } + backTo={`/courses/${courseId}/achievements/`} + title={intl.formatMessage(translations.header, { + title: achievementMiniEntity.title, + })} + > {isLoading ? ( ) : ( @@ -113,13 +109,13 @@ const AchievementShow: FC = (props) => { if (courseUser.obtainedAt !== null) return ( - + - + ); return null; @@ -127,7 +123,7 @@ const AchievementShow: FC = (props) => { ) )} - + ); }; diff --git a/client/app/bundles/course/achievement/pages/AchievementsIndex/index.tsx b/client/app/bundles/course/achievement/pages/AchievementsIndex/index.tsx index 6bbfb04d75e..b29d02b5dc0 100644 --- a/client/app/bundles/course/achievement/pages/AchievementsIndex/index.tsx +++ b/client/app/bundles/course/achievement/pages/AchievementsIndex/index.tsx @@ -3,8 +3,8 @@ import { defineMessages } from 'react-intl'; import { toast } from 'react-toastify'; import AddButton from 'lib/components/core/buttons/AddButton'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import useTranslation from 'lib/hooks/useTranslation'; @@ -37,6 +37,10 @@ const translations = defineMessages({ id: 'course.achievement.AchievementsIndex.toggleFailure', defaultMessage: 'Failed to update achievement.', }, + achievements: { + id: 'course.achievement.AchievementsIndex.achievements', + defaultMessage: 'Achievements', + }, }); const AchievementsIndex: FC = () => { @@ -92,14 +96,11 @@ const AchievementsIndex: FC = () => { }); return ( - <> - + {isLoading ? ( ) : ( @@ -116,8 +117,10 @@ const AchievementsIndex: FC = () => { /> )} - + ); }; -export default AchievementsIndex; +const handle = translations.achievements; + +export default Object.assign(AchievementsIndex, { handle }); diff --git a/client/app/bundles/course/admin/components/SettingsNavigation.tsx b/client/app/bundles/course/admin/components/SettingsNavigation.tsx index 9b3f6ce378d..d9af55245f6 100644 --- a/client/app/bundles/course/admin/components/SettingsNavigation.tsx +++ b/client/app/bundles/course/admin/components/SettingsNavigation.tsx @@ -1,17 +1,26 @@ +import { createContext, useCallback, useContext, useState } from 'react'; +import { defineMessages } from 'react-intl'; import { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from 'react'; -import { Outlet, useLocation, useNavigate } from 'react-router-dom'; + LoaderFunction, + Outlet, + useLoaderData, + useLocation, + useNavigate, +} from 'react-router-dom'; import { Chip } from '@mui/material'; import { CourseAdminItems } from 'types/course/admin/course'; import CourseAPI from 'api/course'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import Preload from 'lib/components/wrappers/Preload'; +import { DataHandle } from 'lib/hooks/router/dynamicNest'; + +const translations = defineMessages({ + courseSettings: { + id: 'course.admin.courseSettings', + defaultMessage: 'Course Settings', + }, +}); const fetchItems = async (): Promise => { const response = await CourseAPI.admin.course.items(); @@ -23,18 +32,10 @@ const ItemsReloaderContext = createContext(() => {}); export const useItemsReloader = (): (() => void) => useContext(ItemsReloaderContext); -interface LoadedSettingsNavigationProps { - data: CourseAdminItems; -} - -const LoadedSettingsNavigation = ( - props: LoadedSettingsNavigationProps, -): JSX.Element => { - const [items, setItems] = useState(props.data); +const SettingsNavigation = (): JSX.Element => { + const data = useLoaderData() as CourseAdminItems; - useEffect(() => { - fetchItems().then(setItems); - }, []); + const [items, setItems] = useState(data); const navigate = useNavigate(); const { pathname } = useLocation(); @@ -46,31 +47,49 @@ const LoadedSettingsNavigation = ( if (!items) return ; return ( - -
-
- {items.map(({ title, path }) => ( - navigate(path)} - variant={path === pathname ? 'filled' : 'outlined'} - /> - ))} -
+ + +
+
+ {items.map(({ title, path }) => ( + navigate(path)} + variant={path === pathname ? 'filled' : 'outlined'} + /> + ))} +
- -
-
+ +
+
+ ); }; -const SettingsNavigation = (): JSX.Element => ( - } while={fetchItems}> - {(data): JSX.Element => } - -); +const loader: LoaderFunction = fetchItems; + +const handle: DataHandle = (match, location) => { + const items = match.data as CourseAdminItems; + const currentItem = items.find(({ path }) => path === location.pathname); + + return { + shouldRevalidate: true, + getData: () => ({ + content: [ + { + title: translations.courseSettings, + }, + { + title: currentItem?.title, + url: currentItem?.path, + }, + ], + }), + }; +}; -export default SettingsNavigation; +export default Object.assign(SettingsNavigation, { loader, handle }); diff --git a/client/app/bundles/course/admin/pages/SidebarSettings/SidebarSettingsForm.tsx b/client/app/bundles/course/admin/pages/SidebarSettings/SidebarSettingsForm.tsx index 93527508561..6b9aa0c8c2f 100644 --- a/client/app/bundles/course/admin/pages/SidebarSettings/SidebarSettingsForm.tsx +++ b/client/app/bundles/course/admin/pages/SidebarSettings/SidebarSettingsForm.tsx @@ -19,6 +19,7 @@ import produce from 'immer'; import { SidebarItem, SidebarItems } from 'types/course/admin/sidebar'; import Section from 'lib/components/core/layouts/Section'; +import { defensivelyGetIcon } from 'lib/constants/icons'; import useTranslation from 'lib/hooks/useTranslation'; import translations from './translations'; @@ -54,7 +55,7 @@ const SidebarSettingsForm = (props: SidebarSettingsFormProps): JSX.Element => { id: item.id, title: item.title, weight: index + 1, - iconClassName: item.iconClassName, + icon: item.icon, })); props.onSubmit(newSidebarItems, setSettings, () => @@ -100,6 +101,8 @@ const SidebarSettingsForm = (props: SidebarSettingsFormProps): JSX.Element => { transform, }; + const Icon = defensivelyGetIcon(item.icon, 'outlined'); + return ( { /> - -
+ + diff --git a/client/app/bundles/course/announcements/components/misc/AnnouncementCard.tsx b/client/app/bundles/course/announcements/components/misc/AnnouncementCard.tsx index 4d8bae09307..a767768e579 100644 --- a/client/app/bundles/course/announcements/components/misc/AnnouncementCard.tsx +++ b/client/app/bundles/course/announcements/components/misc/AnnouncementCard.tsx @@ -2,25 +2,25 @@ import { FC, memo, useState } from 'react'; import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; import { toast } from 'react-toastify'; import { DateRange, PushPin } from '@mui/icons-material'; -import { Link } from '@mui/material'; -import { grey } from '@mui/material/colors'; +import { Paper, Typography } from '@mui/material'; import equal from 'fast-deep-equal'; import { Operation } from 'store'; import { + AnnouncementEntity, AnnouncementFormData, - AnnouncementMiniEntity, } from 'types/course/announcements'; import DeleteButton from 'lib/components/core/buttons/DeleteButton'; import EditButton from 'lib/components/core/buttons/EditButton'; import CustomTooltip from 'lib/components/core/CustomTooltip'; +import Link from 'lib/components/core/Link'; import { useAppDispatch } from 'lib/hooks/store'; import { formatFullDateTime } from 'lib/moment'; import AnnouncementEdit from '../../pages/AnnouncementEdit'; interface Props extends WrappedComponentProps { - announcement: AnnouncementMiniEntity; + announcement: AnnouncementEntity; showEditOptions?: boolean; updateOperation?: ( announcementId: number, @@ -107,30 +107,22 @@ const AnnouncementCard: FC = (props) => { return {announcement.creator.name}; } return ( - + {announcement.creator.name} ); }; - const backgroundColor = announcement.isUnread ? '#ffe8e8' : '#ffffff'; - return ( <> -
@@ -148,7 +140,7 @@ const AnnouncementCard: FC = (props) => { )} -

= (props) => { fontWeight: 'bold', overflowWrap: 'anywhere', }} + variant="h5" > {announcement.title} -

+
{showEditOptions && updateOperation && deleteOperation && (
@@ -186,15 +179,20 @@ const AnnouncementCard: FC = (props) => { )}
- + {formatFullDateTime(announcement.startTime)}{' '} {intl.formatMessage(translations.timeSeparator)} {renderUserLink()} - -
+ -
+ {showEditOptions && updateOperation && ( { + announcements: AnnouncementEntity[], +): AnnouncementEntity[] => { const sortedAnnouncements = [...announcements]; sortedAnnouncements .sort((a, b) => Date.parse(b.startTime) - Date.parse(a.startTime)) diff --git a/client/app/bundles/course/announcements/pages/AnnouncementsIndex/index.tsx b/client/app/bundles/course/announcements/pages/AnnouncementsIndex/index.tsx index c588d208613..e3844a88e55 100644 --- a/client/app/bundles/course/announcements/pages/AnnouncementsIndex/index.tsx +++ b/client/app/bundles/course/announcements/pages/AnnouncementsIndex/index.tsx @@ -1,11 +1,12 @@ -import { FC, useEffect, useState } from 'react'; -import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; +import { useEffect, useState } from 'react'; +import { defineMessages } from 'react-intl'; import { toast } from 'react-toastify'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; import Note from 'lib/components/core/Note'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; +import useTranslation from 'lib/hooks/useTranslation'; import NewAnnouncementButton from '../../components/buttons/NewAnnouncementButton'; import AnnouncementsDisplay from '../../components/misc/AnnouncementsDisplay'; @@ -21,8 +22,6 @@ import { } from '../../selectors'; import AnnouncementNew from '../AnnouncementNew'; -interface Props extends WrappedComponentProps {} - const translations = defineMessages({ fetchAnnouncementsFailure: { id: 'course.announcements.AnnouncementsIndex.fetchAnnouncementsFailure', @@ -42,8 +41,8 @@ const translations = defineMessages({ }, }); -const AnnouncementsIndex: FC = (props) => { - const { intl } = props; +const AnnouncementsIndex = (): JSX.Element => { + const { t } = useTranslation(); // For new announcements form dialog const [isOpen, setIsOpen] = useState(false); @@ -56,33 +55,28 @@ const AnnouncementsIndex: FC = (props) => { useEffect(() => { dispatch(fetchAnnouncements()) - .catch(() => - toast.error(intl.formatMessage(translations.fetchAnnouncementsFailure)), - ) + .catch(() => toast.error(t(translations.fetchAnnouncementsFailure))) .finally(() => setIsLoading(false)); }, [dispatch]); return ( - <> - , - ] - : undefined - } - /> + + ) + } + title={t(translations.header)} + > {isLoading ? ( ) : ( <> {announcements.length === 0 ? ( - + ) : ( = (props) => { /> )} - + ); }; -export default injectIntl(AnnouncementsIndex); +const handle = translations.header; + +export default Object.assign(AnnouncementsIndex, { handle }); diff --git a/client/app/bundles/course/announcements/store.ts b/client/app/bundles/course/announcements/store.ts index f97cd6d6138..f6eb1cb651e 100644 --- a/client/app/bundles/course/announcements/store.ts +++ b/client/app/bundles/course/announcements/store.ts @@ -1,7 +1,6 @@ import produce from 'immer'; import { AnnouncementData, - AnnouncementListData, AnnouncementPermissions, } from 'types/course/announcements'; import { @@ -63,7 +62,7 @@ const reducer = produce( export const actions = { saveAnnouncementList: ( - announcementList: AnnouncementListData[], + announcementList: AnnouncementData[], announcementPermissions: AnnouncementPermissions, ): SaveAnnouncementListAction => { return { diff --git a/client/app/bundles/course/announcements/types.ts b/client/app/bundles/course/announcements/types.ts index f3b38d38a7d..b3460e703cf 100644 --- a/client/app/bundles/course/announcements/types.ts +++ b/client/app/bundles/course/announcements/types.ts @@ -1,7 +1,6 @@ import { AnnouncementData, - AnnouncementListData, - AnnouncementMiniEntity, + AnnouncementEntity, AnnouncementPermissions, } from 'types/course/announcements'; import { EntityStore } from 'types/store'; @@ -15,7 +14,7 @@ export const DELETE_ANNOUNCEMENT = 'course/announcement/DELETE_ANNOUNCEMENT'; // Action Types export interface SaveAnnouncementListAction { type: typeof SAVE_ANNOUNCEMENT_LIST; - announcementList: AnnouncementListData[]; + announcementList: AnnouncementData[]; announcementPermissions: AnnouncementPermissions; } @@ -35,6 +34,6 @@ export type AnnouncementsActionType = // State Types export interface AnnouncementsState { - announcements: EntityStore; + announcements: EntityStore; permissions: AnnouncementPermissions; } diff --git a/client/app/bundles/course/assessment/components/AssessmentForm/operations.ts b/client/app/bundles/course/assessment/components/AssessmentForm/operations.ts index e928754eca2..2306eddfe54 100644 --- a/client/app/bundles/course/assessment/components/AssessmentForm/operations.ts +++ b/client/app/bundles/course/assessment/components/AssessmentForm/operations.ts @@ -1,4 +1,3 @@ -/* eslint-disable import/prefer-default-export */ import { Operation } from 'store'; import CourseAPI from 'api/course'; diff --git a/client/app/bundles/course/assessment/components/FileManager/index.tsx b/client/app/bundles/course/assessment/components/FileManager/index.tsx index f5072104012..0d6ce8495c9 100644 --- a/client/app/bundles/course/assessment/components/FileManager/index.tsx +++ b/client/app/bundles/course/assessment/components/FileManager/index.tsx @@ -7,6 +7,7 @@ import { AxiosError } from 'axios'; import CourseAPI from 'api/course'; import InfoLabel from 'lib/components/core/InfoLabel'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import { getWorkbinFileURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; import { formatLongDateTime } from 'lib/moment'; @@ -175,9 +176,9 @@ const FileManager = (props: FileManagerProps): JSX.Element => { const url = getWorkbinFileURL(getCourseId(), props.folderId, material.id); return ( - + {value} - + ); }; diff --git a/client/app/bundles/course/assessment/handles.ts b/client/app/bundles/course/assessment/handles.ts new file mode 100644 index 00000000000..13eab45b374 --- /dev/null +++ b/client/app/bundles/course/assessment/handles.ts @@ -0,0 +1,93 @@ +import { isAuthenticatedAssessmentData } from 'types/course/assessment/assessments'; +import { getIdFromUnknown } from 'utilities'; + +import { CrumbPath, DataHandle } from 'lib/hooks/router/dynamicNest'; + +import { fetchAssessment, fetchAssessments } from './operations'; + +const getTabTitle = async ( + categoryId?: number, + tabId?: number, +): Promise => { + const { display } = await fetchAssessments(categoryId, tabId); + + return { + activePath: display.tabUrl.split('&tab')[0], + content: { + url: display.tabUrl, + title: display.tabTitle, + }, + }; +}; + +const getTabTitleFromAssessmentId = async ( + assessmentId: number, +): Promise => { + const data = await fetchAssessment(assessmentId); + + return { + activePath: data.tabUrl.split('&tab')[0], + content: { + url: data.tabUrl, + title: data.tabTitle, + }, + }; +}; + +/** + * Gets the crumb data and active path for assessments pages, + * except Submissions and Skills. + */ +export const assessmentsHandle: DataHandle = (match, location) => { + if (location.pathname.includes('assessments/s')) return null; + + let promise: Promise; + + const assessmentId = getIdFromUnknown(match.params?.assessmentId); + if (assessmentId) { + promise = getTabTitleFromAssessmentId(assessmentId); + } else { + const searchParams = new URLSearchParams(location.search); + const categoryId = getIdFromUnknown(searchParams.get('category')); + const tabId = getIdFromUnknown(searchParams.get('tab')); + promise = getTabTitle(categoryId, tabId); + } + + return { shouldRevalidate: true, getData: () => promise }; +}; + +export const assessmentHandle: DataHandle = (match) => { + const assessmentId = getIdFromUnknown(match.params?.assessmentId); + if (!assessmentId) throw new Error(`Invalid assessment id: ${assessmentId}`); + + return { + getData: async (): Promise => { + const data = await fetchAssessment(assessmentId); + return data.title; + }, + }; +}; + +export const questionHandle: DataHandle = (match, location) => { + if (location.pathname.endsWith('new')) return null; + + const assessmentId = getIdFromUnknown(match.params?.assessmentId); + if (!assessmentId) throw new Error(`Invalid assessment id: ${assessmentId}`); + + return { + getData: async (): Promise => { + const data = await fetchAssessment(assessmentId); + if (!isAuthenticatedAssessmentData(data)) return null; + + const question = data.questions?.find( + ({ editUrl }) => editUrl === location.pathname, + ); + + if (!question) return null; + + return question.title + ? `${question.defaultTitle}: ${question.title}` + : question.defaultTitle; + }, + }; +}; diff --git a/client/app/bundles/course/assessment/pages/AssessmentEdit/AssessmentEditPage.jsx b/client/app/bundles/course/assessment/pages/AssessmentEdit/AssessmentEditPage.jsx index 2ef7d0ba32b..402aac2df1d 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentEdit/AssessmentEditPage.jsx +++ b/client/app/bundles/course/assessment/pages/AssessmentEdit/AssessmentEditPage.jsx @@ -5,7 +5,7 @@ import { Navigate } from 'react-router-dom'; import { Button } from '@mui/material'; import PropTypes from 'prop-types'; -import PageHeader from 'lib/components/navigation/PageHeader'; +import Page from 'lib/components/core/layouts/Page'; import { achievementTypesConditionAttributes } from 'lib/types'; import AssessmentForm from '../../components/AssessmentForm'; @@ -67,8 +67,8 @@ class AssessmentEditPage extends Component { // TODO: Add a source router props that can be used to determine where // did the user come from, and initialise a Back button that goes there. return ( -
- + - - + } + className="space-y-5" + title={intl.formatMessage(translations.editAssessment)} + > {this.state.redirectUrl && } -
+ ); } } diff --git a/client/app/bundles/course/assessment/pages/AssessmentEdit/index.tsx b/client/app/bundles/course/assessment/pages/AssessmentEdit/index.tsx index 107a257e9e1..d4f14ab8eab 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentEdit/index.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentEdit/index.tsx @@ -4,6 +4,7 @@ import { getAssessmentId } from 'lib/helpers/url-helpers'; import { DEFAULT_MONITORING_OPTIONS } from '../../constants'; import { fetchAssessmentEditData } from '../../operations'; +import translations from '../../translations'; import { categoryAndTabTitle } from '../../utils'; import AssessmentEditPage from './AssessmentEditPage'; @@ -60,4 +61,6 @@ const AssessmentEdit = (): JSX.Element => { ); }; -export default AssessmentEdit; +const handle = translations.edit; + +export default Object.assign(AssessmentEdit, { handle }); diff --git a/client/app/bundles/course/assessment/pages/AssessmentMonitoring/index.tsx b/client/app/bundles/course/assessment/pages/AssessmentMonitoring/index.tsx index 34992f4d7bf..dbc033cb522 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentMonitoring/index.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentMonitoring/index.tsx @@ -2,6 +2,7 @@ import LoadingIndicator from 'lib/components/core/LoadingIndicator'; import Preload from 'lib/components/wrappers/Preload'; import { fetchMonitoringData } from '../../operations'; +import translations from '../../translations'; import PulseGrid from './PulseGrid'; @@ -13,4 +14,6 @@ const AssessmentMonitoring = (): JSX.Element => { ); }; -export default AssessmentMonitoring; +const handle = translations.pulsegrid; + +export default Object.assign(AssessmentMonitoring, { handle }); diff --git a/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowHeader.tsx b/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowHeader.tsx index 62f851aeddc..c944a2668f8 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowHeader.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowHeader.tsx @@ -15,7 +15,6 @@ import { import DeleteButton from 'lib/components/core/buttons/DeleteButton'; import { PromptText } from 'lib/components/core/dialogs/Prompt'; -import PageHeader from 'lib/components/navigation/PageHeader'; import useTranslation from 'lib/hooks/useTranslation'; import { attemptAssessment, deleteAssessment } from '../../operations'; @@ -83,7 +82,7 @@ const AssessmentShowHeader = ( }; return ( - + <> {assessment.deleteUrl && ( - + @@ -114,10 +110,7 @@ const AssessmentShowHeader = ( {assessment.monitoringUrl && ( - + @@ -130,10 +123,7 @@ const AssessmentShowHeader = ( title={t(translations.assessmentStatistics)} > - + @@ -143,10 +133,7 @@ const AssessmentShowHeader = ( {assessment.submissionsUrl && ( - + @@ -157,16 +144,16 @@ const AssessmentShowHeader = ( )} - + ); }; diff --git a/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowPage.tsx b/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowPage.tsx index 0b4b9a06683..306974bc7f1 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowPage.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentShow/AssessmentShowPage.tsx @@ -11,6 +11,7 @@ import { import { AssessmentData } from 'types/course/assessment/assessments'; import DescriptionCard from 'lib/components/core/DescriptionCard'; +import Page from 'lib/components/core/layouts/Page'; import Subsection from 'lib/components/core/layouts/Subsection'; import Link from 'lib/components/core/Link'; import useTranslation from 'lib/hooks/useTranslation'; @@ -32,9 +33,12 @@ const AssessmentShowPage = (props: AssessmentShowPageProps): JSX.Element => { const { t } = useTranslation(); return ( -
- - + } + backTo={assessment.indexUrl} + className="space-y-5" + title={assessment.title} + > {assessment.status === 'unavailable' && ( )} @@ -166,7 +170,7 @@ const AssessmentShowPage = (props: AssessmentShowPageProps): JSX.Element => { )} )} -
+ ); }; diff --git a/client/app/bundles/course/assessment/pages/AssessmentShow/Question.tsx b/client/app/bundles/course/assessment/pages/AssessmentShow/Question.tsx index fbf196fc2d0..3033ae73834 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentShow/Question.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentShow/Question.tsx @@ -43,8 +43,8 @@ const Question = (props: QuestionProps): JSX.Element => { } ${!dragging ? 'hover?:slot-1-neutral-100' : ''}`} >
diff --git a/client/app/bundles/course/assessment/pages/AssessmentStatistics/index.jsx b/client/app/bundles/course/assessment/pages/AssessmentStatistics/index.jsx index 38a7358833f..10be153672a 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentStatistics/index.jsx +++ b/client/app/bundles/course/assessment/pages/AssessmentStatistics/index.jsx @@ -5,8 +5,8 @@ import { useParams } from 'react-router-dom'; import PropTypes from 'prop-types'; import ErrorCard from 'lib/components/core/ErrorCard'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { fetchAncestors, @@ -24,6 +24,10 @@ import AncestorSelect from './AncestorSelect'; import StatisticsPanel from './StatisticsPanel'; const translations = defineMessages({ + statistics: { + id: 'course.assessment.statistics.statistics', + defaultMessage: 'Statistics', + }, header: { id: 'course.assessment.statistics.header', defaultMessage: 'Statistics for {title}', @@ -164,17 +168,17 @@ const AssessmentStatisticsPage = ({ }; return ( -
- + {renderAncestorSelect()}
{renderAncestorStatistics()}
-
+ ); }; @@ -198,6 +202,11 @@ AssessmentStatisticsPage.propTypes = { ancestorAllStudents: PropTypes.arrayOf(courseUserShape), }; -export default connect(({ assessments }) => assessments.statisticsPage)( - injectIntl(AssessmentStatisticsPage), +const handle = translations.statistics; + +export default Object.assign( + connect(({ assessments }) => assessments.statisticsPage)( + injectIntl(AssessmentStatisticsPage), + ), + { handle }, ); diff --git a/client/app/bundles/course/assessment/pages/AssessmentStatistics/utils.js b/client/app/bundles/course/assessment/pages/AssessmentStatistics/utils.js index 988c26a05b1..fe6c388bb16 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentStatistics/utils.js +++ b/client/app/bundles/course/assessment/pages/AssessmentStatistics/utils.js @@ -8,7 +8,6 @@ function processDayDifference(dayDifference) { return `D+${dayDifference}`; } -// eslint-disable-next-line import/prefer-default-export export function processSubmissionsIntoChartData(submissions) { const submittedSubmissions = submissions.filter((s) => s.submittedAt != null); const mappedSubmissions = submittedSubmissions diff --git a/client/app/bundles/course/assessment/pages/AssessmentsIndex/ActionButtons.tsx b/client/app/bundles/course/assessment/pages/AssessmentsIndex/ActionButtons.tsx index 0c40b70bc53..8ff0f1317c0 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentsIndex/ActionButtons.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentsIndex/ActionButtons.tsx @@ -56,35 +56,22 @@ const ActionButtons = (props: ActionButtonsProps): JSX.Element => { .catch(() => setAttempting(false)); }; - const actionButton = actionButtonUrl && ( - - - - ); - return (
- {student ? ( - actionButton - ) : ( -
- {actionButton} -
+ {actionButtonUrl && ( + + + )} {assessment.editUrl && ( diff --git a/client/app/bundles/course/assessment/pages/AssessmentsIndex/AssessmentsTable.tsx b/client/app/bundles/course/assessment/pages/AssessmentsIndex/AssessmentsTable.tsx index 2d44db24918..65721cc563c 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentsIndex/AssessmentsTable.tsx +++ b/client/app/bundles/course/assessment/pages/AssessmentsIndex/AssessmentsTable.tsx @@ -1,18 +1,18 @@ -import { Link } from 'react-router-dom'; import { AssessmentListData, AssessmentsListData, } from 'types/course/assessment/assessments'; +import Link from 'lib/components/core/Link'; import Note from 'lib/components/core/Note'; import PersonalStartEndTime from 'lib/components/extensions/PersonalStartEndTime'; +import StackedBadges from 'lib/components/extensions/StackedBadges'; import Table, { ColumnTemplate } from 'lib/components/table'; import useTranslation from 'lib/hooks/useTranslation'; import translations from '../../translations'; import ActionButtons from './ActionButtons'; -import StackedBadges from './StackedBadges'; import StatusBadges from './StatusBadges'; interface AssessmentsTableProps { @@ -31,9 +31,9 @@ const AssessmentsTable = (props: AssessmentsTableProps): JSX.Element => {
); }; diff --git a/client/app/bundles/course/courses/pages/CourseShow/index.tsx b/client/app/bundles/course/courses/pages/CourseShow/index.tsx index 172d96b9a82..6c88463a537 100644 --- a/client/app/bundles/course/courses/pages/CourseShow/index.tsx +++ b/client/app/bundles/course/courses/pages/CourseShow/index.tsx @@ -2,11 +2,12 @@ import { FC, useEffect, useState } from 'react'; import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; import { useParams } from 'react-router-dom'; import { toast } from 'react-toastify'; -import { Grid } from '@mui/material'; +import { Typography } from '@mui/material'; +import { CourseEntity } from 'types/course/courses'; import AvatarWithLabel from 'lib/components/core/AvatarWithLabel'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import CourseAnnouncements from '../../components/misc/CourseAnnouncements'; @@ -33,6 +34,13 @@ const translations = defineMessages({ }, }); +const getShouldShowEnrolOptions = (course: CourseEntity): boolean => { + const info = course.registrationInfo; + if (!info) return false; + + return info.isDisplayCodeForm || info.isEnrollable; +}; + const CourseShow: FC = (props) => { const { intl } = props; const [isLoading, setIsLoading] = useState(true); @@ -59,54 +67,61 @@ const CourseShow: FC = (props) => { } return ( - <> - - + {!course.permissions.isCurrentCourseUser && ( <> - - -
- {course.registrationInfo && ( - - )} -
-
- -

+ {getShouldShowEnrolOptions(course) && ( +
+ +
+ )} + + {course.description.trim() && ( +
+ {intl.formatMessage(translations.descriptionHeader)} -

-
-
-
-

{intl.formatMessage(translations.instructorsHeader)}

- - {course.instructors?.map((instructor) => ( - -
+ + + + + )} + +
+ + {intl.formatMessage(translations.instructorsHeader)} + + +
+ {course.instructors?.map((instructor) => ( +
- - ))} - + ))} +
+
)} {(course.permissions.isCurrentCourseUser || course.permissions.canManage) && ( <> - + {Boolean(course.currentlyActiveAnnouncements?.length) && ( + + )} {course.assessmentTodos && ( = (props) => { )} - + ); }; diff --git a/client/app/bundles/course/courses/pages/CoursesIndex/index.tsx b/client/app/bundles/course/courses/pages/CoursesIndex/index.tsx index d72a564b423..b9a211c488d 100644 --- a/client/app/bundles/course/courses/pages/CoursesIndex/index.tsx +++ b/client/app/bundles/course/courses/pages/CoursesIndex/index.tsx @@ -1,10 +1,11 @@ import { FC, ReactElement, useEffect, useState } from 'react'; import { defineMessages } from 'react-intl'; +import { useSearchParams } from 'react-router-dom'; import { toast } from 'react-toastify'; import { Button } from '@mui/material'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import useTranslation from 'lib/hooks/useTranslation'; @@ -50,7 +51,9 @@ const translations = defineMessages({ const CoursesIndex: FC = () => { const { t } = useTranslation(); - const [isNewCourseDialogOpen, setIsNewCourseDialogOpen] = useState(false); + + const [params] = useSearchParams(); + const [isLoading, setIsLoading] = useState(true); const [isRoleRequestDialogOpen, setRoleRequestDialogOpen] = useState(false); const courses = useAppSelector(getAllCourseMiniEntities); @@ -63,6 +66,13 @@ const CoursesIndex: FC = () => { const dispatch = useAppDispatch(); + const shouldOpenNewCourseDialog = + Boolean(params.get('new')) && coursesPermissions?.canCreate; + + const [isNewCourseDialogOpen, setIsNewCourseDialogOpen] = useState( + shouldOpenNewCourseDialog, + ); + useEffect(() => { dispatch(fetchCourses()) .finally(() => setIsLoading(false)) @@ -102,8 +112,7 @@ const CoursesIndex: FC = () => { } return ( - <> - + {isLoading ? ( ) : ( @@ -124,8 +133,10 @@ const CoursesIndex: FC = () => { )} )} - + ); }; -export default CoursesIndex; +const handle = translations.header; + +export default Object.assign(CoursesIndex, { handle }); diff --git a/client/app/bundles/course/discussion/topics/components/cards/CommentCard.tsx b/client/app/bundles/course/discussion/topics/components/cards/CommentCard.tsx index 24130ddf5c5..0d1af9a3ee6 100644 --- a/client/app/bundles/course/discussion/topics/components/cards/CommentCard.tsx +++ b/client/app/bundles/course/discussion/topics/components/cards/CommentCard.tsx @@ -9,12 +9,13 @@ import { toast } from 'react-toastify'; import Delete from '@mui/icons-material/Delete'; import Edit from '@mui/icons-material/Edit'; import { LoadingButton } from '@mui/lab'; -import { Avatar, Button, CardHeader, Link } from '@mui/material'; +import { Avatar, Button, CardHeader } from '@mui/material'; import { grey, orange, red } from '@mui/material/colors'; import { CommentPostMiniEntity } from 'types/course/comments'; import ConfirmationDialog from 'lib/components/core/dialogs/ConfirmationDialog'; import CKEditorRichText from 'lib/components/core/fields/CKEditorRichText'; +import Link from 'lib/components/core/Link'; import { useAppDispatch } from 'lib/hooks/store'; import { formatLongDateTime } from 'lib/moment'; @@ -195,12 +196,14 @@ const CommentCard: FC = (props) => { avatar={ } + classes={{ avatar: 'mr-4' }} style={{ padding: 6 }} subheader={`${formatLongDateTime(post.createdAt)}${ post.isDelayed ? ' (delayed comment)' : '' @@ -208,7 +211,9 @@ const CommentCard: FC = (props) => { subheaderTypographyProps={{ display: 'block' }} title={ post.creator.userUrl ? ( - {post.creator.name} + + {post.creator.name} + ) : ( post.creator.name ) diff --git a/client/app/bundles/course/discussion/topics/components/cards/TopicCard.tsx b/client/app/bundles/course/discussion/topics/components/cards/TopicCard.tsx index f694190ab95..8c9a5eb1b93 100644 --- a/client/app/bundles/course/discussion/topics/components/cards/TopicCard.tsx +++ b/client/app/bundles/course/discussion/topics/components/cards/TopicCard.tsx @@ -1,16 +1,18 @@ import { FC, lazy, Suspense, useEffect, useState } from 'react'; -import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; +import { defineMessages } from 'react-intl'; import { CheckCircleOutline, PendingOutlined, ScheduleOutlined, } from '@mui/icons-material'; -import { Card, CardContent, CardHeader, Link } from '@mui/material'; +import { Card, CardContent, CardHeader, Typography } from '@mui/material'; import { CommentStatusTypes, CommentTopicEntity } from 'types/course/comments'; +import Link from 'lib/components/core/Link'; import { getCourseUserURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; +import useTranslation from 'lib/hooks/useTranslation'; import { updatePending, updateRead } from '../../operations'; import { getAllCommentPostMiniEntities } from '../../selectors'; @@ -25,14 +27,14 @@ const CommentField = lazy( ), ); -interface Props extends WrappedComponentProps { +interface TopicCardProps { topic: CommentTopicEntity; } const translations = defineMessages({ - by: { - id: 'course.discussion.topics.TopicCard.by', - defaultMessage: 'By', + byCreator: { + id: 'course.discussion.topics.TopicCard.byCreator', + defaultMessage: 'Created by {creatorName}', }, pendingStatus: { id: 'course.discussion.topics.TopicCard.pendingStatus', @@ -56,8 +58,10 @@ const translations = defineMessages({ }, }); -const TopicCard: FC = (props) => { - const { intl, topic } = props; +const TopicCard: FC = (props) => { + const { topic } = props; + const { t } = useTranslation(); + const dispatch = useAppDispatch(); const postListData = useAppSelector(getAllCommentPostMiniEntities).filter( (post) => post.topicId === topic.id, @@ -130,7 +134,7 @@ const TopicCard: FC = (props) => { }} > - {intl.formatMessage(translations.loadingStatus)} + {t(translations.loadingStatus)}
); case CommentStatusTypes.pending: @@ -147,7 +151,7 @@ const TopicCard: FC = (props) => { }} > - {intl.formatMessage(translations.pendingStatus)} + {t(translations.pendingStatus)} ); case CommentStatusTypes.notPending: @@ -164,7 +168,7 @@ const TopicCard: FC = (props) => { }} > - {intl.formatMessage(translations.notPendingStatus)} + {t(translations.notPendingStatus)} ); case CommentStatusTypes.read: @@ -183,7 +187,7 @@ const TopicCard: FC = (props) => { }} > - {intl.formatMessage(translations.unreadStatus)} + {t(translations.unreadStatus)} ); default: @@ -196,28 +200,38 @@ const TopicCard: FC = (props) => { -
{renderStatus()}
-
- {`${intl.formatMessage(translations.by)}: `} - - {topic.creator.name} - -
- +
+ + {t(translations.byCreator, { + creatorName: topic.creator.name, + link: (chunk) => ( + + {chunk} + + ), + })} + + + {renderStatus()} +
} title={ - {topic.timestamp ? `${topic.title}: ${topic.timestamp.toString()}` : topic.title} - + } /> @@ -246,7 +260,7 @@ const TopicCard: FC = (props) => { marginTop: 10, }} > - {intl.formatMessage(translations.loadingComment)} + {t(translations.loadingComment)}
} > @@ -258,4 +272,4 @@ const TopicCard: FC = (props) => { ); }; -export default injectIntl(TopicCard); +export default TopicCard; diff --git a/client/app/bundles/course/discussion/topics/pages/CommentIndex/index.tsx b/client/app/bundles/course/discussion/topics/pages/CommentIndex/index.tsx index 938e2177194..23b58efadf3 100644 --- a/client/app/bundles/course/discussion/topics/pages/CommentIndex/index.tsx +++ b/client/app/bundles/course/discussion/topics/pages/CommentIndex/index.tsx @@ -15,9 +15,9 @@ import { CommentTabTypes, } from 'types/course/comments'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; import CustomBadge from 'lib/components/extensions/CustomBadge'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import TopicList from '../../components/lists/TopicList'; @@ -199,24 +199,29 @@ const CommentIndex: FC = (props) => { }, [dispatch]); return ( - <> - + {isLoading ? ( ) : ( <> - + + + + )} - + ); }; -export default injectIntl(CommentIndex); +const handle = translations.comments; + +export default Object.assign(injectIntl(CommentIndex), { handle }); diff --git a/client/app/bundles/course/duplication/components/BulkSelectors.jsx b/client/app/bundles/course/duplication/components/BulkSelectors.jsx index 8f3adcaab98..ec5aad17343 100644 --- a/client/app/bundles/course/duplication/components/BulkSelectors.jsx +++ b/client/app/bundles/course/duplication/components/BulkSelectors.jsx @@ -1,6 +1,8 @@ import { defineMessages, FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; +import Link from 'lib/components/core/Link'; + const translations = defineMessages({ selectAll: { id: 'course.duplication.BulkSelectors.selectAll', @@ -25,18 +27,18 @@ const styles = { const BulkSelectors = ({ callback, styles: userStyles = {} }) => ( <> - callback(true)} style={{ ...styles.selectLink, ...userStyles.selectLink }} > - - + callback(false)} style={{ ...styles.deselectLink, ...userStyles.deselectLink }} > - + ); diff --git a/client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/index.jsx b/client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/index.jsx index 44cc1fe869a..3a94d9d2b98 100644 --- a/client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/index.jsx +++ b/client/app/bundles/course/duplication/pages/Duplication/DuplicateItemsConfirmation/index.jsx @@ -9,6 +9,7 @@ import { duplicateItems } from 'course/duplication/operations'; import { courseShape } from 'course/duplication/propTypes'; import { actions } from 'course/duplication/store'; import ConfirmationDialog from 'lib/components/core/dialogs/ConfirmationDialog'; +import Link from 'lib/components/core/Link'; import AchievementsListing from './AchievementsListing'; import AssessmentsListing from './AssessmentsListing'; @@ -83,11 +84,9 @@ class DuplicateItemsConfirmation extends Component { -

- - {destinationCourse.title} - -

+ + {destinationCourse.title} +
diff --git a/client/app/bundles/course/duplication/pages/Duplication/index.jsx b/client/app/bundles/course/duplication/pages/Duplication/index.jsx index 46cbed98840..285f0b50712 100644 --- a/client/app/bundles/course/duplication/pages/Duplication/index.jsx +++ b/client/app/bundles/course/duplication/pages/Duplication/index.jsx @@ -7,6 +7,7 @@ import { Paper, Radio, RadioGroup, + Typography, } from '@mui/material'; import PropTypes from 'prop-types'; @@ -22,8 +23,8 @@ import { } from 'course/duplication/propTypes'; import { actions } from 'course/duplication/store'; import DateTimePicker from 'lib/components/core/fields/DateTimePicker'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import DestinationCourseSelector from './DestinationCourseSelector'; import DuplicateAllButton from './DuplicateAllButton'; @@ -81,9 +82,6 @@ const styles = { gridTemplateColumns: '210px auto', gridTemplateRows: 'auto', }, - sidebar: { - padding: '25px 20px', - }, itemsSidebarHeader: { padding: '25px 20px 0px 20px', }, @@ -133,19 +131,22 @@ class Duplication extends Component { return (
-
-

+
+ -

+
+ {this.renderFromCourseMain()} -
{this.renderToCourseSidebar()}
+
{this.renderToCourseSidebar()}
+ {this.renderItemsSelectorSidebar()} + {duplicationMode === duplicationModes.OBJECT && isCourseSelected ? ( @@ -195,11 +196,7 @@ class Duplication extends Component { const { duplicationMode, isCourseSelected } = this.props; if (duplicationMode === duplicationModes.COURSE) { - return ( -
- -
- ); + return ; } if (isCourseSelected) { return ( @@ -225,22 +222,14 @@ class Duplication extends Component { > } - label={ - - - - } + control={} + label={} value={duplicationModes.COURSE} /> } - label={ - - - - } + control={} + label={} value={duplicationModes.OBJECT} /> @@ -250,9 +239,9 @@ class Duplication extends Component { renderToCourseSidebar() { const { dispatch, modesAllowed } = this.props; const header = ( -

+ -

+ ); const isSingleValidMode = @@ -274,12 +263,9 @@ class Duplication extends Component { render() { return ( - <> - } - /> + }> {this.renderBody()} - + ); } } @@ -300,15 +286,20 @@ Duplication.propTypes = { intl: PropTypes.object, }; -export default connect(({ duplication }) => ({ - isLoading: duplication.isLoading, - isChangingCourse: duplication.isChangingCourse, - isCourseSelected: !!duplication.destinationCourseId, - duplicationMode: duplication.duplicationMode, - modesAllowed: duplication.sourceCourse.duplicationModesAllowed, - enabledComponents: duplication.sourceCourse.enabledComponents, - currentHost: duplication.currentHost, - currentCourseId: duplication.currentCourseId, - sourceCourse: duplication.sourceCourse, - sourceCourses: duplication.sourceCourses, -}))(injectIntl(Duplication)); +const handle = translations.duplicateData; + +export default Object.assign( + connect(({ duplication }) => ({ + isLoading: duplication.isLoading, + isChangingCourse: duplication.isChangingCourse, + isCourseSelected: !!duplication.destinationCourseId, + duplicationMode: duplication.duplicationMode, + modesAllowed: duplication.sourceCourse.duplicationModesAllowed, + enabledComponents: duplication.sourceCourse.enabledComponents, + currentHost: duplication.currentHost, + currentCourseId: duplication.currentCourseId, + sourceCourse: duplication.sourceCourse, + sourceCourses: duplication.sourceCourses, + }))(injectIntl(Duplication)), + { handle }, +); diff --git a/client/app/bundles/course/enrol-requests/pages/UserRequests/index.tsx b/client/app/bundles/course/enrol-requests/pages/UserRequests/index.tsx index a27c387efcd..1c353f6c2cc 100644 --- a/client/app/bundles/course/enrol-requests/pages/UserRequests/index.tsx +++ b/client/app/bundles/course/enrol-requests/pages/UserRequests/index.tsx @@ -1,11 +1,10 @@ import { FC, useEffect, useState } from 'react'; import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; import { toast } from 'react-toastify'; -import { Box } from '@mui/material'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; import Note from 'lib/components/core/Note'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import UserManagementTabs from '../../../users/components/navigation/UserManagementTabs'; @@ -83,15 +82,16 @@ const UserRequests: FC = (props) => { rejectedEnrolRequests.length === 0 ) { return ( - + + + ); } return undefined; }; return ( - - + {isLoading ? ( ) : ( @@ -127,8 +127,10 @@ const UserRequests: FC = (props) => { )} )} - + ); }; -export default injectIntl(UserRequests); +const handle = translations.manageUsersHeader; + +export default Object.assign(injectIntl(UserRequests), { handle }); diff --git a/client/app/bundles/course/experience-points/disbursement/components/forms/DisbursementForm.tsx b/client/app/bundles/course/experience-points/disbursement/components/forms/DisbursementForm.tsx index 53914157379..ab8c0a5a807 100644 --- a/client/app/bundles/course/experience-points/disbursement/components/forms/DisbursementForm.tsx +++ b/client/app/bundles/course/experience-points/disbursement/components/forms/DisbursementForm.tsx @@ -17,6 +17,7 @@ import { import * as yup from 'yup'; import ErrorText from 'lib/components/core/ErrorText'; +import Page from 'lib/components/core/layouts/Page'; import FormTextField from 'lib/components/form/fields/TextField'; import { setReactHookFormError } from 'lib/helpers/react-hook-form-helper'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; @@ -174,7 +175,7 @@ const DisbursementForm: FC = (props) => { }; return ( - <> + = (props) => { - + + + + - + ); }; diff --git a/client/app/bundles/course/experience-points/disbursement/components/forms/ForumDisbursementForm.tsx b/client/app/bundles/course/experience-points/disbursement/components/forms/ForumDisbursementForm.tsx index 5a8dc39ab91..09570b2999f 100644 --- a/client/app/bundles/course/experience-points/disbursement/components/forms/ForumDisbursementForm.tsx +++ b/client/app/bundles/course/experience-points/disbursement/components/forms/ForumDisbursementForm.tsx @@ -19,6 +19,7 @@ import { import * as yup from 'yup'; import ErrorText from 'lib/components/core/ErrorText'; +import Page from 'lib/components/core/layouts/Page'; import FormTextField from 'lib/components/form/fields/TextField'; import { setReactHookFormError } from 'lib/helpers/react-hook-form-helper'; import { useAppDispatch } from 'lib/hooks/store'; @@ -162,10 +163,13 @@ const ForumDisbursementForm: FC = (props) => { - + + + + ); diff --git a/client/app/bundles/course/experience-points/disbursement/components/tables/DisbursementTable.tsx b/client/app/bundles/course/experience-points/disbursement/components/tables/DisbursementTable.tsx index 955fdb4d848..d07b2cee48f 100644 --- a/client/app/bundles/course/experience-points/disbursement/components/tables/DisbursementTable.tsx +++ b/client/app/bundles/course/experience-points/disbursement/components/tables/DisbursementTable.tsx @@ -5,6 +5,7 @@ import { TableColumns, TableOptions } from 'types/components/DataTable'; import { DisbursementCourseUserMiniEntity } from 'types/course/disbursement'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import { DEFAULT_TABLE_ROWS_PER_PAGE } from 'lib/constants/sharedConstants'; import { getCourseUserURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; @@ -77,13 +78,12 @@ const DisbursementTable: FC = (props: Props) => { }, }), customBodyRenderLite: (dataIndex): JSX.Element => ( - {filteredUsers[dataIndex].name} - + ), }, }, diff --git a/client/app/bundles/course/experience-points/disbursement/components/tables/ForumDisbursementTable.tsx b/client/app/bundles/course/experience-points/disbursement/components/tables/ForumDisbursementTable.tsx index 7e34cae6255..0b3dd61fc69 100644 --- a/client/app/bundles/course/experience-points/disbursement/components/tables/ForumDisbursementTable.tsx +++ b/client/app/bundles/course/experience-points/disbursement/components/tables/ForumDisbursementTable.tsx @@ -6,12 +6,12 @@ import { WrappedComponentProps, } from 'react-intl'; import { Tooltip } from 'react-tooltip'; -import { Link } from '@mui/material'; import equal from 'fast-deep-equal'; import { TableColumns, TableOptions } from 'types/components/DataTable'; import { ForumDisbursementUserEntity } from 'types/course/disbursement'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import { DEFAULT_TABLE_ROWS_PER_PAGE } from 'lib/constants/sharedConstants'; import { getCourseUserURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; @@ -89,13 +89,12 @@ const ForumDisbursementTable: FC = (props: Props) => { style: { overflowWrap: 'anywhere', padding: '5px 14px' }, }), customBodyRenderLite: (dataIndex): JSX.Element => ( - {forumUsers[dataIndex].name} - + ), }, }, @@ -165,12 +164,8 @@ const ForumDisbursementTable: FC = (props: Props) => { { - onPostClick(forumUsers[dataIndex]); - }} + onClick={(): void => onPostClick(forumUsers[dataIndex])} > {forumUsers[dataIndex].postCount} diff --git a/client/app/bundles/course/experience-points/disbursement/components/tables/ForumPostTable.tsx b/client/app/bundles/course/experience-points/disbursement/components/tables/ForumPostTable.tsx index 22e5d1656b4..e8a3ed7b772 100644 --- a/client/app/bundles/course/experience-points/disbursement/components/tables/ForumPostTable.tsx +++ b/client/app/bundles/course/experience-points/disbursement/components/tables/ForumPostTable.tsx @@ -5,6 +5,7 @@ import { TableColumns, TableOptions } from 'types/components/DataTable'; import { ForumDisbursementPostEntity } from 'types/course/disbursement'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import { getForumTopicURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; import { formatLongDateTime } from 'lib/moment'; @@ -73,15 +74,16 @@ const ForumPostTable: FC = (props: Props) => { customBodyRenderLite: (dataIndex): JSX.Element => { const post = data[dataIndex]; return ( - {post.title} - + ); }, }, diff --git a/client/app/bundles/course/experience-points/disbursement/pages/DisbursementIndex/index.tsx b/client/app/bundles/course/experience-points/disbursement/pages/DisbursementIndex/index.tsx index 22befe6b7af..26e25c0a0fc 100644 --- a/client/app/bundles/course/experience-points/disbursement/pages/DisbursementIndex/index.tsx +++ b/client/app/bundles/course/experience-points/disbursement/pages/DisbursementIndex/index.tsx @@ -8,11 +8,11 @@ import { import { toast } from 'react-toastify'; import { Group } from '@mui/icons-material'; import FormatListBulletedIcon from '@mui/icons-material/FormatListBulleted'; -import { Grid, Tab, Tabs } from '@mui/material'; +import { Tab, Tabs } from '@mui/material'; import palette from 'theme/palette'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch } from 'lib/hooks/store'; import { fetchDisbursements, fetchForumDisbursements } from '../../operations'; @@ -58,8 +58,7 @@ const DisbursementIndex: FC = (props) => { }, [dispatch]); return ( - <> - + {isLoading ? ( ) : ( @@ -71,7 +70,6 @@ const DisbursementIndex: FC = (props) => { style={{ backgroundColor: palette.background.default, }} - sx={{ marginBottom: 2 }} TabIndicatorProps={{ color: 'primary', style: { height: 5 } }} value={tabValue} variant="fullWidth" @@ -91,30 +89,18 @@ const DisbursementIndex: FC = (props) => { value="general-disbursement-tab" /> - + + {tabValue === 'general-disbursement-tab' ? ( - - + ) : ( - + )} )} - + ); }; -export default injectIntl(DisbursementIndex); +const handle = translations.disbursements; + +export default Object.assign(injectIntl(DisbursementIndex), { handle }); diff --git a/client/app/bundles/course/experience-points/disbursement/pages/ForumDisbursement/index.tsx b/client/app/bundles/course/experience-points/disbursement/pages/ForumDisbursement/index.tsx index 7bef25377c7..430ebcd4e94 100644 --- a/client/app/bundles/course/experience-points/disbursement/pages/ForumDisbursement/index.tsx +++ b/client/app/bundles/course/experience-points/disbursement/pages/ForumDisbursement/index.tsx @@ -12,6 +12,8 @@ import { } from '@mui/material'; import { ForumDisbursementUserEntity } from 'types/course/disbursement'; +import Page from 'lib/components/core/layouts/Page'; +import Link from 'lib/components/core/Link'; import { getCourseUserURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; @@ -68,17 +70,16 @@ const ForumDisbursement: FC = () => { }; return ( - <> + { startDate: formatLongDateTime(filters.startTime), endDate: formatLongDateTime(filters.endTime), })}{' '} - {selectedForumPostUser.name} - +
setSelectedForumPostUser(null)}> @@ -143,7 +141,7 @@ const ForumDisbursement: FC = () => { )} - + ); }; diff --git a/client/app/bundles/course/forum/components/buttons/NextUnreadButton.tsx b/client/app/bundles/course/forum/components/buttons/NextUnreadButton.tsx index 1953e80485e..08d01e957fd 100644 --- a/client/app/bundles/course/forum/components/buttons/NextUnreadButton.tsx +++ b/client/app/bundles/course/forum/components/buttons/NextUnreadButton.tsx @@ -1,8 +1,8 @@ import { FC } from 'react'; import { defineMessages } from 'react-intl'; -import { Link } from 'react-router-dom'; import { Button, Tooltip } from '@mui/material'; +import Link from 'lib/components/core/Link'; import useTranslation from 'lib/hooks/useTranslation'; interface Props { diff --git a/client/app/bundles/course/forum/components/cards/PostCard.tsx b/client/app/bundles/course/forum/components/cards/PostCard.tsx index 2a53aad770b..eb78dff0446 100644 --- a/client/app/bundles/course/forum/components/cards/PostCard.tsx +++ b/client/app/bundles/course/forum/components/cards/PostCard.tsx @@ -10,6 +10,7 @@ import { } from '@mui/material'; import CKEditorRichText from 'lib/components/core/fields/CKEditorRichText'; +import Link from 'lib/components/core/Link'; import { useAppSelector } from 'lib/hooks/store'; import { formatLongDateTime } from 'lib/moment'; @@ -76,20 +77,16 @@ const PostCard: FC = (props) => { avatar={postCreatorObject.avatar} className="pb-0" subheader={formatLongDateTime(post.createdAt)} - subheaderTypographyProps={{ variant: 'body1' }} title={ <> - {postCreatorObject.userUrl ? ( - - {postCreatorObject.name} - - ) : ( - postCreatorObject.name - )} + + {postCreatorObject.name} + + {postCreatorObject.visibilityIcon} } diff --git a/client/app/bundles/course/forum/components/misc/PostCreatorObject.tsx b/client/app/bundles/course/forum/components/misc/PostCreatorObject.tsx index 166264ec521..a321cb7528b 100644 --- a/client/app/bundles/course/forum/components/misc/PostCreatorObject.tsx +++ b/client/app/bundles/course/forum/components/misc/PostCreatorObject.tsx @@ -1,8 +1,9 @@ import { useState } from 'react'; import { defineMessages } from 'react-intl'; import { Visibility, VisibilityOff } from '@mui/icons-material'; -import { Avatar, IconButton, Link } from '@mui/material'; +import { Avatar, IconButton } from '@mui/material'; +import Link from 'lib/components/core/Link'; import useTranslation from 'lib/hooks/useTranslation'; interface PostCreatorProps { @@ -58,8 +59,9 @@ const PostCreatorObject = (props: PostCreatorProps): PostCreatorReturnProps => { alt={creator.name} className="h-20 w-20" component={Link} - href={creator.userUrl} src={creator.imageUrl} + to={creator.userUrl} + underline="none" /> ), name: creator.name, @@ -74,8 +76,9 @@ const PostCreatorObject = (props: PostCreatorProps): PostCreatorReturnProps => { alt={creator.name} className="h-20 w-20" component={Link} - href={creator.userUrl} src={creator.imageUrl} + to={creator.userUrl} + underline="none" /> ), name: creator.name, diff --git a/client/app/bundles/course/forum/components/tables/ForumTable.tsx b/client/app/bundles/course/forum/components/tables/ForumTable.tsx index 114103b72d2..11a9ba88458 100644 --- a/client/app/bundles/course/forum/components/tables/ForumTable.tsx +++ b/client/app/bundles/course/forum/components/tables/ForumTable.tsx @@ -1,6 +1,5 @@ import { FC, memo } from 'react'; import { defineMessages } from 'react-intl'; -import { Link } from 'react-router-dom'; import Email from '@mui/icons-material/Email'; import Help from '@mui/icons-material/Help'; import { Tooltip, Typography } from '@mui/material'; @@ -9,6 +8,7 @@ import { TableColumns, TableOptions } from 'types/components/DataTable'; import { ForumEntity } from 'types/course/forums'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import Note from 'lib/components/core/Note'; import CustomBadge from 'lib/components/extensions/CustomBadge'; import useTranslation from 'lib/hooks/useTranslation'; diff --git a/client/app/bundles/course/forum/components/tables/ForumTopicTable.tsx b/client/app/bundles/course/forum/components/tables/ForumTopicTable.tsx index 0ca8cab97fd..f8b704c4bc6 100644 --- a/client/app/bundles/course/forum/components/tables/ForumTopicTable.tsx +++ b/client/app/bundles/course/forum/components/tables/ForumTopicTable.tsx @@ -1,6 +1,5 @@ import { FC, memo } from 'react'; import { defineMessages } from 'react-intl'; -import { Link } from 'react-router-dom'; import { Campaign, CheckCircle, @@ -15,6 +14,7 @@ import { TableColumns, TableOptions } from 'types/components/DataTable'; import { ForumEntity, ForumTopicEntity } from 'types/course/forums'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import Note from 'lib/components/core/Note'; import useTranslation from 'lib/hooks/useTranslation'; import { formatLongDateTime } from 'lib/moment'; @@ -212,17 +212,9 @@ const ForumTopicTable: FC = (props) => {
{t(translations.startedBy)}{' '} - {postCreatorObject.userUrl ? ( - - {postCreatorObject.name} - - ) : ( - postCreatorObject.name - )} + + {postCreatorObject.name} + {postCreatorObject.visibilityIcon}
@@ -265,17 +257,10 @@ const ForumTopicTable: FC = (props) => { }); return ( <> - {postCreatorObject.userUrl ? ( - - {postCreatorObject.name} - - ) : ( - postCreatorObject.name - )} + + {postCreatorObject.name} + + {postCreatorObject.visibilityIcon}
{formatLongDateTime(latestPostCreator.createdAt)} diff --git a/client/app/bundles/course/forum/handles.ts b/client/app/bundles/course/forum/handles.ts new file mode 100644 index 00000000000..d3a262c5814 --- /dev/null +++ b/client/app/bundles/course/forum/handles.ts @@ -0,0 +1,32 @@ +import CourseAPI from 'api/course'; +import { DataHandle } from 'lib/hooks/router/dynamicNest'; + +const getForumTitle = async (forumId: string): Promise => { + const response = await CourseAPI.forum.forums.fetch(forumId); + return response.data.forum.name; +}; + +const getTopicTitle = async ( + forumId: string, + topicId: string, +): Promise => { + const response = await CourseAPI.forum.topics.fetch(forumId, topicId); + return response.data.topic.title; +}; + +export const forumHandle: DataHandle = (match) => { + const forumId = match.params?.forumId; + if (!forumId) throw new Error(`Invalid forum id: ${forumId}`); + + return { getData: () => getForumTitle(forumId) }; +}; + +export const forumTopicHandle: DataHandle = (match) => { + const forumId = match.params?.forumId; + if (!forumId) throw new Error(`Invalid forum id: ${forumId}`); + + const topicId = match.params?.topicId; + if (!topicId) throw new Error(`Invalid topic id: ${topicId}`); + + return { getData: () => getTopicTitle(forumId, topicId) }; +}; diff --git a/client/app/bundles/course/forum/pages/ForumShow/index.tsx b/client/app/bundles/course/forum/pages/ForumShow/index.tsx index 605d22bc99c..82569fc43f8 100644 --- a/client/app/bundles/course/forum/pages/ForumShow/index.tsx +++ b/client/app/bundles/course/forum/pages/ForumShow/index.tsx @@ -4,9 +4,8 @@ import { useParams } from 'react-router-dom'; import { toast } from 'react-toastify'; import AddButton from 'lib/components/core/buttons/AddButton'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; -import ScrollToTop from 'lib/components/navigation/ScrollToTop'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import useTranslation from 'lib/hooks/useTranslation'; @@ -118,14 +117,12 @@ const ForumShow: FC = () => { const forumPageHeaderTitle = forum ? forum.name : t(translations.header); return ( - <> - - - + {!isLoading && isOpen && ( { ) : ( )} - + ); }; diff --git a/client/app/bundles/course/forum/pages/ForumTopicShow/index.tsx b/client/app/bundles/course/forum/pages/ForumTopicShow/index.tsx index 170b63524c6..38cb08157a4 100644 --- a/client/app/bundles/course/forum/pages/ForumTopicShow/index.tsx +++ b/client/app/bundles/course/forum/pages/ForumTopicShow/index.tsx @@ -5,10 +5,9 @@ import { toast } from 'react-toastify'; import { Box } from '@mui/material'; import { TopicType } from 'types/course/forums'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; import Note from 'lib/components/core/Note'; -import PageHeader from 'lib/components/navigation/PageHeader'; -import ScrollToTop from 'lib/components/navigation/ScrollToTop'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import useTranslation from 'lib/hooks/useTranslation'; @@ -137,16 +136,13 @@ const ForumTopicShow: FC = () => { ); return ( - <> - - - + {isLoading ? : renderBody} - + ); }; diff --git a/client/app/bundles/course/forum/pages/ForumsIndex/index.tsx b/client/app/bundles/course/forum/pages/ForumsIndex/index.tsx index ed21bde0c27..1f2072c4640 100644 --- a/client/app/bundles/course/forum/pages/ForumsIndex/index.tsx +++ b/client/app/bundles/course/forum/pages/ForumsIndex/index.tsx @@ -3,9 +3,8 @@ import { defineMessages } from 'react-intl'; import { toast } from 'react-toastify'; import AddButton from 'lib/components/core/buttons/AddButton'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; -import ScrollToTop from 'lib/components/navigation/ScrollToTop'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import useTranslation from 'lib/hooks/useTranslation'; @@ -99,18 +98,19 @@ const ForumsIndex: FC = () => { ); return ( - <> - - + {!isLoading && isForumNewDialogOpen && ( setIsForumNewDialogOpen(false)} open={isForumNewDialogOpen} /> )} + {isLoading ? : } - + ); }; -export default ForumsIndex; +const handle = translations.header; + +export default Object.assign(ForumsIndex, { handle }); diff --git a/client/app/bundles/course/group/pages/GroupIndex/index.jsx b/client/app/bundles/course/group/pages/GroupIndex/index.jsx index dbe4f80f020..79f2e7fe842 100644 --- a/client/app/bundles/course/group/pages/GroupIndex/index.jsx +++ b/client/app/bundles/course/group/pages/GroupIndex/index.jsx @@ -1,14 +1,15 @@ import { useEffect, useState } from 'react'; import { defineMessages, injectIntl } from 'react-intl'; -import { Link, Outlet, useNavigate, useParams } from 'react-router-dom'; +import { Outlet, useNavigate, useParams } from 'react-router-dom'; import { toast } from 'react-toastify'; import { Tab, Tabs } from '@mui/material'; import PropTypes from 'prop-types'; import CourseAPI from 'api/course'; +import Page from 'lib/components/core/layouts/Page'; +import Link from 'lib/components/core/Link'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; import Note from 'lib/components/core/Note'; -import PageHeader from 'lib/components/navigation/PageHeader'; import GroupNew from '../GroupNew'; @@ -68,6 +69,7 @@ const GroupIndex = (props) => { {groupCategories.groupCategories.map((category) => ( { ) : ( <> - {renderTabs} + {renderTabs} ); return ( - <> - + {isLoading ? : renderBody} - + ); }; @@ -102,4 +103,6 @@ GroupIndex.propTypes = { intl: PropTypes.object.isRequired, }; -export default injectIntl(GroupIndex); +const handle = translations.groups; + +export default Object.assign(injectIntl(GroupIndex), { handle }); diff --git a/client/app/bundles/course/leaderboard/components/tables/LeaderboardTable.tsx b/client/app/bundles/course/leaderboard/components/tables/LeaderboardTable.tsx index 03cc0091d9e..1c49297cd72 100644 --- a/client/app/bundles/course/leaderboard/components/tables/LeaderboardTable.tsx +++ b/client/app/bundles/course/leaderboard/components/tables/LeaderboardTable.tsx @@ -4,7 +4,6 @@ import { Avatar, AvatarGroup, Box, - Link, Tooltip, useMediaQuery, } from '@mui/material'; @@ -17,6 +16,7 @@ import { } from 'types/course/leaderboard'; import DataTable from 'lib/components/core/layouts/DataTable'; +import Link from 'lib/components/core/Link'; import { getAchievementURL, getCourseUserURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; @@ -145,15 +145,14 @@ const LeaderboardTable: FC = (props: Props) => { )} marginRight={1} src={individualData[dataIndex].imageUrl} + underline="none" /> - {individualData[dataIndex].name} - + ), }, @@ -239,6 +238,7 @@ const LeaderboardTable: FC = (props: Props) => { href={getAchievementURL(getCourseId(), achievement.id)} id={`achievement_${achievement.id}`} src={achievement.badge.url} + underline="none" /> ); @@ -298,6 +298,7 @@ const LeaderboardTable: FC = (props: Props) => { component={Link} href={getCourseUserURL(getCourseId(), user.id)} src={user.imageUrl} + underline="none" /> ))} diff --git a/client/app/bundles/course/leaderboard/pages/LeaderboardIndex/index.tsx b/client/app/bundles/course/leaderboard/pages/LeaderboardIndex/index.tsx index aa525dfff28..9935f2650f7 100644 --- a/client/app/bundles/course/leaderboard/pages/LeaderboardIndex/index.tsx +++ b/client/app/bundles/course/leaderboard/pages/LeaderboardIndex/index.tsx @@ -12,8 +12,8 @@ import { useTheme } from '@mui/material/styles'; import useMediaQuery from '@mui/material/useMediaQuery'; import palette from 'theme/palette'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; import LeaderboardTable from '../../components/tables/LeaderboardTable'; @@ -80,14 +80,14 @@ const LeaderboardIndex: FC = (props) => { const isGroupHidden = groupLeaderboardPoints.length === 0; return ( - <> - + {isLoading ? ( ) : ( @@ -218,8 +218,10 @@ const LeaderboardIndex: FC = (props) => { )} - + ); }; -export default injectIntl(LeaderboardIndex); +const handle = translations.leaderboard; + +export default Object.assign(injectIntl(LeaderboardIndex), { handle }); diff --git a/client/app/bundles/course/learning-map/components/Node/index.jsx b/client/app/bundles/course/learning-map/components/Node/index.jsx index 535167dfbbb..d67152dba66 100644 --- a/client/app/bundles/course/learning-map/components/Node/index.jsx +++ b/client/app/bundles/course/learning-map/components/Node/index.jsx @@ -6,7 +6,8 @@ import { Card, CardContent } from '@mui/material'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import PropTypes from 'prop-types'; -import { COURSE_COMPONENT_ICONS } from 'lib/constants/sharedConstants'; +import Link from 'lib/components/core/Link'; +import { defensivelyGetIcon } from 'lib/constants/icons'; import { nodeShape } from '../../propTypes'; import translations from '../../translations'; @@ -85,7 +86,7 @@ const Node = (props) => { setIsNodeMenuDisplayed(true); }; - const IconComponent = COURSE_COMPONENT_ICONS[node.courseMaterialType]; + const Icon = defensivelyGetIcon(node.courseMaterialType); return (
@@ -107,19 +108,15 @@ const Node = (props) => { />
)} - + {!canModify && !node.unlocked && }
{canModify && ( ({ - isLoading: lessonPlan.lessonPlan.isLoading, - groups: lessonPlan.lessonPlan.groups, -}))(LessonPlanLayout); +const handle = translations.lessonPlan; + +export default Object.assign( + connect(({ lessonPlan }) => ({ + isLoading: lessonPlan.lessonPlan.isLoading, + groups: lessonPlan.lessonPlan.groups, + }))(LessonPlanLayout), + { handle }, +); diff --git a/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/ItemRow/index.jsx b/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/ItemRow/index.jsx index 519cf251d02..c6ae37e71f2 100644 --- a/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/ItemRow/index.jsx +++ b/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/ItemRow/index.jsx @@ -3,6 +3,8 @@ import { defineMessages, FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; +import Link from 'lib/components/core/Link'; + import { fields } from '../../../constants'; import { updateItem } from '../../../operations'; @@ -67,7 +69,9 @@ class ItemRow extends Component { return ( {columnsVisible[fields.ITEM_TYPE] ? {type} : null} - {itemPath ? {title} : title} + + {title} + {columnsVisible[fields.START_AT] ? ( ) : null} diff --git a/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/index.jsx b/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/index.jsx index 12d7e27f81a..580248d3978 100644 --- a/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/index.jsx +++ b/client/app/bundles/course/lesson-plan/pages/LessonPlanEdit/index.jsx @@ -1,10 +1,9 @@ import { Component } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; -import { Card, CardContent } from '@mui/material'; import PropTypes from 'prop-types'; -import PageHeader from 'lib/components/navigation/PageHeader'; +import Page from 'lib/components/core/layouts/Page'; import { lessonPlanTypesGroups } from 'lib/types'; import { fields } from '../../constants'; @@ -55,18 +54,6 @@ export class LessonPlanEdit extends Component { return rows; }; - // eslint-disable-next-line class-methods-use-this - renderHeader = () => ( - - - - - - - - - ); - renderTableHeader() { const { columnsVisible } = this.props; @@ -93,18 +80,26 @@ export class LessonPlanEdit extends Component { const { groups } = this.props; return ( - <> - } /> - - {this.props.canManageLessonPlan && this.renderHeader()} - + + + + + + + ) + } + title={} + >
{this.renderTableHeader()} {groups.map(this.renderGroup)}
- +
); } } diff --git a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/AdminTools.jsx b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/AdminTools.jsx index 5751f80c376..7ff13f41489 100644 --- a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/AdminTools.jsx +++ b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/AdminTools.jsx @@ -4,7 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import Delete from '@mui/icons-material/Delete'; import Edit from '@mui/icons-material/Edit'; -import { Button } from '@mui/material'; +import { IconButton } from '@mui/material'; import PropTypes from 'prop-types'; import { showDeleteConfirmation } from 'lib/actions'; @@ -38,16 +38,9 @@ const translations = defineMessages({ const styles = { tools: { top: 16, - right: 66, + right: 20, position: 'absolute', }, - edit: { - minWidth: 40, - }, - delete: { - minWidth: 40, - marginLeft: 10, - }, }; class AdminTools extends PureComponent { @@ -115,21 +108,13 @@ class AdminTools extends PureComponent { return ( - - + ); } diff --git a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/Details/index.jsx b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/Details/index.jsx index d6381f230ea..869513223e8 100644 --- a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/Details/index.jsx +++ b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/LessonPlanItem/Details/index.jsx @@ -2,6 +2,8 @@ import { PureComponent } from 'react'; import { CardContent, CardHeader } from '@mui/material'; import PropTypes from 'prop-types'; +import Link from 'lib/components/core/Link'; + import Chips from './Chips'; class Details extends PureComponent { @@ -16,7 +18,13 @@ class Details extends PureComponent { renderTitle() { const { title, itemPath } = this.props; return ( - {title} : title} /> + + {title} + + } + /> ); } diff --git a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/MilestoneAdminTools.jsx b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/MilestoneAdminTools.jsx index 16006fd8813..e4fc28a6ab1 100644 --- a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/MilestoneAdminTools.jsx +++ b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/MilestoneAdminTools.jsx @@ -4,7 +4,7 @@ import { defineMessages, injectIntl } from 'react-intl'; import { connect } from 'react-redux'; import Delete from '@mui/icons-material/Delete'; import Edit from '@mui/icons-material/Edit'; -import { Button } from '@mui/material'; +import { IconButton } from '@mui/material'; import PropTypes from 'prop-types'; import { showDeleteConfirmation } from 'lib/actions'; @@ -35,17 +35,6 @@ const translations = defineMessages({ }, }); -const styles = { - edit: { - minWidth: 40, - }, - delete: { - minWidth: 40, - marginLeft: 10, - marginRight: 20, - }, -}; - class MilestoneAdminTools extends PureComponent { deleteMilestoneHandler = () => { const { @@ -98,21 +87,13 @@ class MilestoneAdminTools extends PureComponent { return ( - - + ); } diff --git a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/index.jsx b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/index.jsx index ccb15194b94..9e968ee3b35 100644 --- a/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/index.jsx +++ b/client/app/bundles/course/lesson-plan/pages/LessonPlanShow/index.jsx @@ -2,10 +2,9 @@ import { Component } from 'react'; import { FormattedMessage } from 'react-intl'; import { connect } from 'react-redux'; import { scroller } from 'react-scroll'; -import { Card, CardContent } from '@mui/material'; import PropTypes from 'prop-types'; -import PageHeader from 'lib/components/navigation/PageHeader'; +import Page from 'lib/components/core/layouts/Page'; import moment from 'lib/moment'; import { lessonPlanTypesGroups } from 'lib/types'; @@ -81,25 +80,22 @@ export class LessonPlanShow extends Component { ); } - // eslint-disable-next-line class-methods-use-this - renderHeader = () => ( - - - - - - - - ); - render() { return ( - <> - } /> - - {this.props.canManageLessonPlan && this.renderHeader()} + + + + + + ) + } + title={} + > {this.props.groups.map((group) => this.renderGroup(group))} - + ); } } diff --git a/client/app/bundles/course/level/components/LevelRow.jsx b/client/app/bundles/course/level/components/LevelRow.jsx index da75c349d75..4a9d756f493 100644 --- a/client/app/bundles/course/level/components/LevelRow.jsx +++ b/client/app/bundles/course/level/components/LevelRow.jsx @@ -1,7 +1,7 @@ import { Component } from 'react'; import { defineMessages, FormattedMessage } from 'react-intl'; import Delete from '@mui/icons-material/Delete'; -import { Button, TableCell, TableRow, TextField } from '@mui/material'; +import { IconButton, TableCell, TableRow, TextField } from '@mui/material'; import PropTypes from 'prop-types'; const translations = defineMessages({ @@ -34,17 +34,17 @@ class LevelRow extends Component { const { deleteLevel, disabled, levelNumber } = this.props; return ( - + ); } diff --git a/client/app/bundles/course/level/pages/LevelsIndex/index.jsx b/client/app/bundles/course/level/pages/LevelsIndex/index.jsx index c8ae125298f..805f9fa1620 100644 --- a/client/app/bundles/course/level/pages/LevelsIndex/index.jsx +++ b/client/app/bundles/course/level/pages/LevelsIndex/index.jsx @@ -14,8 +14,8 @@ import { import PropTypes from 'prop-types'; import { defaultComponentTitles } from 'course/translations.intl'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import LevelRow from '../../components/LevelRow'; import { @@ -217,16 +217,16 @@ class LevelsIndex extends Component { render() { return ( - <> - - } - /> + + } + unpadded + > {this.props.isLoading ? : this.renderBody()} - + ); } } @@ -240,9 +240,14 @@ LevelsIndex.propTypes = { dispatch: PropTypes.func.isRequired, }; -export default connect(({ levels }) => ({ - canManage: levels.canManage, - isLoading: levels.isLoading, - isSaving: levels.isSaving, - levels: levels.levels, -}))(LevelsIndex); +const handle = translations.levelHeader; + +export default Object.assign( + connect(({ levels }) => ({ + canManage: levels.canManage, + isLoading: levels.isLoading, + isSaving: levels.isSaving, + levels: levels.levels, + }))(LevelsIndex), + { handle }, +); diff --git a/client/app/bundles/course/material/folders/components/tables/TableMaterialRow.tsx b/client/app/bundles/course/material/folders/components/tables/TableMaterialRow.tsx index f63f6bee631..5d4715ca81a 100644 --- a/client/app/bundles/course/material/folders/components/tables/TableMaterialRow.tsx +++ b/client/app/bundles/course/material/folders/components/tables/TableMaterialRow.tsx @@ -4,6 +4,7 @@ import { Stack, TableCell, TableRow } from '@mui/material'; import equal from 'fast-deep-equal'; import { MaterialMiniEntity } from 'types/course/material/folders'; +import Link from 'lib/components/core/Link'; import { getCourseId } from 'lib/helpers/url-helpers'; import { formatFullDateTime } from 'lib/moment'; @@ -21,22 +22,22 @@ const TableMaterialRow: FC = (props) => { return ( - + - {material.name} - + {material.description !== null && material.description.length !== 0 && ( @@ -55,21 +56,21 @@ const TableMaterialRow: FC = (props) => { - +
{formatFullDateTime(material.updatedAt)}
- {material.updater.name} + + {material.updater.name} +
{!isCurrentCourseStudent && ( = (props) => { - )} - + = (props) => { return ( - + @@ -55,6 +55,7 @@ const TableSubfolderRow: FC = (props) => { to={`/courses/${getCourseId()}/materials/folders/${ subfolder.id }/`} + underline="hover" > {`${subfolder.name} (${subfolder.itemCount})`} @@ -88,7 +89,6 @@ const TableSubfolderRow: FC = (props) => { = (props) => { {!isCurrentCourseStudent && ( = (props) => { )} = (props) => { }; return ( - + - - {columnHeaderWithSort('Name')} - - - {columnHeaderWithSort('Last Modified')} - + {columnHeaderWithSort('Name')} + {columnHeaderWithSort('Last Modified')} {!isCurrentCourseStudent && ( - - {columnHeaderWithSort('Start At')} - + {columnHeaderWithSort('Start At')} )} @@ -167,7 +162,7 @@ const WorkbinTable: FC = (props) => { ); })} -
+ ); }; diff --git a/client/app/bundles/course/material/folders/handles.ts b/client/app/bundles/course/material/folders/handles.ts new file mode 100644 index 00000000000..a3e04c8e8c5 --- /dev/null +++ b/client/app/bundles/course/material/folders/handles.ts @@ -0,0 +1,44 @@ +import { getIdFromUnknown } from 'utilities'; + +import CourseAPI from 'api/course'; +import { CrumbPath, DataHandle } from 'lib/hooks/router/dynamicNest'; + +const getFolderTitle = async ( + courseUrl: string, + folderId: number, +): Promise => { + const { data } = await CourseAPI.folders.fetch(folderId); + + const workbinUrl = `${courseUrl}/materials/folders/${data.breadcrumbs[0].id}`; + + return { + activePath: workbinUrl, + content: data.breadcrumbs.map((crumb) => ({ + title: crumb.name, + url: `materials/folders/${crumb.id}`, + })), + }; +}; + +/** + * `shouldRevalidate` here relies on the invariant that `folderHandle` is attached to + * a route that is stable across different folders, i.e., `/materials/folders`. This route + * will be the pathname that the Dynamic Nest API's builder uses to reconcile the children + * of the Workbin's crumb. + * + * Note that Workbin has only ONE crumb, but multiple children in its `content`. This strategy + * is due to the fact that the Workbin's URL does not reflect any information of the nesting of + * the folders. Thus, `useMatches` cannot notify `useDynamicNest` of the changes in the routes, + * e.g., `useDynamicNest` cannot know if we move out from Folder 2 to Folder 1 from the URL. + */ +export const folderHandle: DataHandle = (match) => { + const folderId = getIdFromUnknown(match.params?.folderId); + if (!folderId) throw new Error(`Invalid folder id: ${folderId}`); + + const courseUrl = `/courses/${match.params.courseId}`; + + return { + shouldRevalidate: true, + getData: () => getFolderTitle(courseUrl, folderId), + }; +}; diff --git a/client/app/bundles/course/material/folders/operations.ts b/client/app/bundles/course/material/folders/operations.ts index 400049ab9ea..5405d5c36e7 100644 --- a/client/app/bundles/course/material/folders/operations.ts +++ b/client/app/bundles/course/material/folders/operations.ts @@ -71,7 +71,6 @@ export function loadFolder(folderId: number): Operation { data.currFolderInfo, data.subfolders, data.materials, - data.breadcrumbs, data.advanceStartAt, data.permissions, ), @@ -93,7 +92,6 @@ export function createFolder( data.currFolderInfo, data.subfolders, data.materials, - data.breadcrumbs, data.advanceStartAt, data.permissions, ), @@ -114,7 +112,6 @@ export function updateFolder( data.currFolderInfo, data.subfolders, data.materials, - data.breadcrumbs, data.advanceStartAt, data.permissions, ), @@ -167,7 +164,6 @@ export function uploadMaterials( data.currFolderInfo, data.subfolders, data.materials, - data.breadcrumbs, data.advanceStartAt, data.permissions, ), diff --git a/client/app/bundles/course/material/folders/pages/FolderShow/index.tsx b/client/app/bundles/course/material/folders/pages/FolderShow/index.tsx index 5ccae448ed1..e042b2a4255 100644 --- a/client/app/bundles/course/material/folders/pages/FolderShow/index.tsx +++ b/client/app/bundles/course/material/folders/pages/FolderShow/index.tsx @@ -1,12 +1,10 @@ import { FC, ReactElement, useEffect, useState } from 'react'; import { defineMessages } from 'react-intl'; -import { Link, useParams } from 'react-router-dom'; -import { Breadcrumbs, Paper } from '@mui/material'; -import { grey } from '@mui/material/colors'; +import { useParams } from 'react-router-dom'; import EditButton from 'lib/components/core/buttons/EditButton'; +import Page from 'lib/components/core/layouts/Page'; import LoadingIndicator from 'lib/components/core/LoadingIndicator'; -import PageHeader from 'lib/components/navigation/PageHeader'; import { getWorkbinFolderURL } from 'lib/helpers/url-builders'; import { getCourseId } from 'lib/helpers/url-helpers'; import { useAppDispatch, useAppSelector } from 'lib/hooks/store'; @@ -19,7 +17,6 @@ import MaterialUpload from '../../components/misc/MaterialUpload'; import WorkbinTable from '../../components/tables/WorkbinTable'; import { loadFolder } from '../../operations'; import { - getBreadcrumbs, getCurrFolderInfo, getFolderMaterials, getFolderPermissions, @@ -50,7 +47,6 @@ const FolderShow: FC = () => { const subfolders = useAppSelector(getFolderSubfolders); const materials = useAppSelector(getFolderMaterials); const currFolderInfo = useAppSelector(getCurrFolderInfo); - const breadcrumbs = useAppSelector(getBreadcrumbs); const permissions = useAppSelector(getFolderPermissions); const [isLoading, setIsLoading] = useState(true); @@ -114,52 +110,20 @@ const FolderShow: FC = () => { }; return ( - <> - - - {breadcrumbs.map((breadcrumb, index) => { - if (index === breadcrumbs.length - 1) { - return ( - - {breadcrumb.name} - - ); - } - return ( - - {breadcrumb.name} - - ); - })} - - - + { handleClose={(): void => setIsMaterialUploadOpen(false)} isOpen={isMaterialUploadOpen} /> - + ); }; diff --git a/client/app/bundles/course/material/folders/selectors.ts b/client/app/bundles/course/material/folders/selectors.ts index 31d98ee47df..6de5dbe3686 100644 --- a/client/app/bundles/course/material/folders/selectors.ts +++ b/client/app/bundles/course/material/folders/selectors.ts @@ -24,10 +24,6 @@ export function getFolderMaterials(state: AppState) { ); } -export function getBreadcrumbs(state: AppState) { - return getLocalState(state).breadcrumbs; -} - export function getAdvanceStartAt(state: AppState) { return getLocalState(state).advanceStartAt; } diff --git a/client/app/bundles/course/material/folders/store.ts b/client/app/bundles/course/material/folders/store.ts index 2b789daebf5..63c026adbd8 100644 --- a/client/app/bundles/course/material/folders/store.ts +++ b/client/app/bundles/course/material/folders/store.ts @@ -35,7 +35,6 @@ const initialState: FoldersState = { }, subfolders: createEntityStore(), materials: createEntityStore(), - breadcrumbs: [], advanceStartAt: 0, permissions: { isCurrentCourseStudent: false, @@ -66,7 +65,6 @@ const reducer = produce((draft: FoldersState, action: FoldersActionType) => { saveListToStore(draft.materials, materialsEntityList); draft.advanceStartAt = action.advanceStartAt; - draft.breadcrumbs = action.breadcrumbs; draft.permissions = action.permissions; break; } @@ -114,7 +112,6 @@ export const actions = { }, subfolders: FolderListData[], materials: MaterialListData[], - breadcrumbs: { id: number; name: string }[], advanceStartAt: number, permissions: FolderPermissions, ): SaveFolderAction => { @@ -123,7 +120,6 @@ export const actions = { currFolderInfo, subfolders, materials, - breadcrumbs, advanceStartAt, permissions, }; diff --git a/client/app/bundles/course/material/folders/types.ts b/client/app/bundles/course/material/folders/types.ts index fba9fb3f365..f8309731629 100644 --- a/client/app/bundles/course/material/folders/types.ts +++ b/client/app/bundles/course/material/folders/types.ts @@ -29,7 +29,6 @@ export interface SaveFolderAction { subfolders: FolderListData[]; materials: MaterialListData[]; - breadcrumbs: { id: number; name: string }[]; advanceStartAt: number; permissions: FolderPermissions; } @@ -67,7 +66,6 @@ export interface FoldersState { subfolders: EntityStore; materials: EntityStore; - breadcrumbs: { id: number; name: string }[]; advanceStartAt: number; permissions: FolderPermissions; } diff --git a/client/app/bundles/course/reference-timelines/TimelineDesigner.tsx b/client/app/bundles/course/reference-timelines/TimelineDesigner.tsx index 951971d8a27..fb46a67a843 100644 --- a/client/app/bundles/course/reference-timelines/TimelineDesigner.tsx +++ b/client/app/bundles/course/reference-timelines/TimelineDesigner.tsx @@ -5,6 +5,7 @@ import { useAppDispatch } from 'lib/hooks/store'; import DayView from './views/DayView'; import { LastSavedProvider } from './contexts'; import { fetchTimelines } from './operations'; +import translations from './translations'; const TimelineDesigner = (): JSX.Element => { const dispatch = useAppDispatch(); @@ -21,4 +22,6 @@ const TimelineDesigner = (): JSX.Element => { ); }; -export default TimelineDesigner; +const handle = translations.timelineDesigner; + +export default Object.assign(TimelineDesigner, { handle }); diff --git a/client/app/bundles/course/reference-timelines/components/DayCalendar/DayCalendar.tsx b/client/app/bundles/course/reference-timelines/components/DayCalendar/DayCalendar.tsx index 918d15f674e..719499bdd2c 100644 --- a/client/app/bundles/course/reference-timelines/components/DayCalendar/DayCalendar.tsx +++ b/client/app/bundles/course/reference-timelines/components/DayCalendar/DayCalendar.tsx @@ -54,7 +54,7 @@ const DayCalendar = forwardRef( return (
-