diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index c63eee68..ad83154a 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -262,6 +262,30 @@ class ExercisesController < ApplicationController else current_user.id end + + # Order of elements is important and will be kept + available_tips = ExerciseTip.where(exercise: @exercise) + .order(rank: :asc, parent_exercise_tip_id: :asc) + + # Transform result set in a hash and prepare (temporary) children array. + # The children array will contain the sorted list of nested tips, + # shown for learners in the output sidebar with cards. + # Hash - Key: exercise_tip.id, value: exercise_tip Object loaded from database + nested_tips = available_tips.each_with_object({}) do |exercise_tip, hash| + exercise_tip.children = [] + hash[exercise_tip.id] = exercise_tip + end + + available_tips.each do |tip| + # A tip without a parent cannot be a children + next if tip.parent_exercise_tip_id.blank? + + # Link tips if they are related + nested_tips[tip.parent_exercise_tip_id].children << tip + end + + # Return an array with top-level tips + @tips = nested_tips.values.select { |tip| tip.parent_exercise_tip_id.nil? } end def set_course_token diff --git a/app/javascript/packs/highlight.js b/app/javascript/packs/highlight.js index 690511fe..e1e56a41 100644 --- a/app/javascript/packs/highlight.js +++ b/app/javascript/packs/highlight.js @@ -12,4 +12,4 @@ import hljs from 'highlight.js' window.hljs = hljs; // CSS -import 'highlight.js/styles/default.css' +import 'highlight.js/styles/tomorrow.css' diff --git a/app/views/exercises/_editor_output.html.slim b/app/views/exercises/_editor_output.html.slim index 75855cb2..353e288f 100644 --- a/app/views/exercises/_editor_output.html.slim +++ b/app/views/exercises/_editor_output.html.slim @@ -55,6 +55,8 @@ div.h-100 id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-bottom ul.body #output.mt-2 pre = t('exercises.implement.no_output_yet') + - unless @embed_options[:disable_hints] or @tips.blank? + = render(partial: 'tips_content') - if CodeOcean::Config.new(:code_ocean).read[:flowr][:enabled] && !@embed_options[:disable_hints] && !@embed_options[:hide_test_results] #flowrHint.card.text-white.bg-info data-url=CodeOcean::Config.new(:code_ocean).read[:flowr][:url] role='tab' .card-header = t('exercises.implement.flowr.heading') diff --git a/app/views/exercises/_tips_content.html.slim b/app/views/exercises/_tips_content.html.slim new file mode 100644 index 00000000..7abcc628 --- /dev/null +++ b/app/views/exercises/_tips_content.html.slim @@ -0,0 +1,13 @@ +- content_for :head do + // Force a full page reload, see https://github.com/turbolinks/turbolinks/issues/326. + Otherwise, code might not be highlighted correctly (race condition) + meta name='turbolinks-visit-control' content='reload' + = javascript_pack_tag('highlight', 'data-turbolinks-track': true) + = stylesheet_pack_tag('highlight', media: 'all', 'data-turbolinks-track': true) + +#tips.card.text-white.bg-info.mt-2 role="tab" style="display: block;" + .card-header.py-2 + i.fa.fa-lightbulb + = t('exercises.implement.tips.heading') + .card-body.text-dark.bg-white.p-2 + = render(partial: 'tips/collapsed_card', collection: @tips, as: :exercise_tip) diff --git a/app/views/tips/_collapsed_card.html.slim b/app/views/tips/_collapsed_card.html.slim new file mode 100644 index 00000000..e6edd319 --- /dev/null +++ b/app/views/tips/_collapsed_card.html.slim @@ -0,0 +1,24 @@ +- tip = exercise_tip.tip +.card.mb-2 + .card-header.p-2 id="tip-heading-#{exercise_tip.id}" role="tab" + .card-title.mb-0 + a.collapsed aria-controls="tip-collapse-#{exercise_tip.id}" aria-expanded="false" data-parent="#tips" data-toggle="collapse" href="#tip-collapse-#{exercise_tip.id}" + .clearfix role="button" + i.fa aria-hidden="true" + span + = t('activerecord.models.tip.one') + =< exercise_tip.rank + = ": #{tip.title}" if tip.title? + .card.card-collapse.collapse id="tip-collapse-#{exercise_tip.id}" aria-labelledby="tip-heading-#{exercise_tip.id}" role="tabpanel" + .card-body.p-3 + h5 + = t('exercises.implement.tips.description') + = tip.description + - if tip.example? + h5.mt-2 + = t('exercises.implement.tips.example') + pre + code.mh-100 class="language-#{tip.file_type.editor_mode.gsub("ace/mode/", "")}" + = tip.example + .mb-4 + = render(partial: 'tips/collapsed_card', collection: exercise_tip.children, as: :exercise_tip) diff --git a/config/locales/de.yml b/config/locales/de.yml index 28690b79..cf9d6c51 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -125,6 +125,11 @@ de: title: Titel description: Beschreibung example: Beispiel + exercise_tip: + tip: Tipp + exercise: Aufgabe + rank: Rang + parent_exercise_tip: Übergeordneter Tipp file_template: name: "Name" file_type: "Dateityp" @@ -207,6 +212,9 @@ de: study_group: one: Lerngruppe other: Lerngruppen + tip: + one: Tipp + other: Tipps tag: one: Tag other: Tags @@ -407,6 +415,10 @@ de: text: "Uns ist aufgefallen, dass du schon lange an dieser Aufgabe arbeitest. Möchtest du vielleicht später weiter machen um erstmal auf neue Gedanken zu kommen?" error_hints: heading: "Hinweise" + tips: + heading: Tipps + description: Erklärung + example: Beispiel flowr: heading: "Weitere Hinweise | Unterstützt von StackOverflow" go_to_question: "Lösung ansehen" diff --git a/config/locales/en.yml b/config/locales/en.yml index 65c68950..5f127574 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -122,9 +122,14 @@ en: usage: Used difficulty: Share on the Exercise tip: - title: title - description: description - example: example + title: Title + description: Description + example: Example + exercise_tip: + tip: Tip + exercise: Exercise + rank: Rank + parent_exercise_tip: Parent Tip file_template: name: "Name" file_type: "File Type" @@ -207,6 +212,9 @@ en: study_group: one: Study Group other: Study Groups + tip: + one: Tip + other: Tips tag: one: Tag other: Tags @@ -407,6 +415,10 @@ en: text: "We recognized that you are already working quite a while on this exercise. We would like to encourage you to take a break and come back later." error_hints: heading: "Hints" + tips: + heading: Tips + description: Description + example: Example flowr: heading: "Gain more insights here | Powered by StackOverflow" go_to_question: "Go to answer"