mumuki-laboratory 8.5.0 → 8.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +31 -8
  3. data/app/controllers/application_controller.rb +1 -1
  4. data/app/controllers/exam_authorization_requests_controller.rb +26 -0
  5. data/app/controllers/exam_registrations_controller.rb +6 -0
  6. data/app/helpers/application_helper.rb +4 -0
  7. data/app/helpers/icons_helper.rb +3 -11
  8. data/app/helpers/menu_bar_helper.rb +2 -2
  9. data/app/helpers/progress_bar_helper.rb +2 -2
  10. data/app/views/chapters/show.html.erb +2 -2
  11. data/app/views/discussions/_message.html.erb +7 -7
  12. data/app/views/exam_authorization_requests/show.html.erb +17 -0
  13. data/app/views/exam_registrations/show.html.erb +37 -0
  14. data/app/views/exercises/show.html.erb +1 -1
  15. data/app/views/layouts/_guide.html.erb +3 -3
  16. data/app/views/layouts/_progress_bar.html.erb +9 -7
  17. data/app/views/layouts/_progress_listing.html.erb +5 -5
  18. data/app/views/layouts/application.html.erb +1 -6
  19. data/app/views/notifications/_discussion.html.erb +1 -0
  20. data/app/views/notifications/_dropdown.html.erb +13 -0
  21. data/app/views/notifications/_exam_authorization_request.html.erb +1 -0
  22. data/app/views/notifications/_exam_registration.html.erb +1 -0
  23. data/app/views/notifications/_message.html.erb +1 -0
  24. data/config/routes.rb +3 -0
  25. data/lib/mumuki/laboratory/controllers/notifications.rb +3 -22
  26. data/lib/mumuki/laboratory/locales/en.yml +28 -14
  27. data/lib/mumuki/laboratory/locales/es-CL.yml +20 -6
  28. data/lib/mumuki/laboratory/locales/es.yml +25 -12
  29. data/lib/mumuki/laboratory/locales/pt.yml +22 -8
  30. data/lib/mumuki/laboratory/version.rb +1 -1
  31. data/spec/controllers/exam_authorization_requests_controller_spec.rb +40 -0
  32. data/spec/controllers/exam_registrations_controller_spec.rb +19 -0
  33. data/spec/features/notifications_flow_spec.rb +46 -0
  34. metadata +113 -98
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7c7290ee8af41b2af9f9d34977a9df9095fd3fb38a74e167bb1678b754cc001
4
- data.tar.gz: 364699a1f019910da6ccc23d6970d20053a2ea36de3351fd32368c31395df89a
3
+ metadata.gz: 92a4fb878a7767d1fe4b760252213ae327d8e5ae82bab805065eddb85b3baa36
4
+ data.tar.gz: e45d454449b183f1f4c200152a25f2df58ef385c1c07949e3fda7be94e6de57c
5
5
  SHA512:
6
- metadata.gz: b1f8f15e334cfc4599eab6b8872c829553fe2c92559bc5a22f796a23df711caf233317982ca0c057eaf8e8c0c9ddbc4209e1f3454ac9cfa379f79ce07a154265
7
- data.tar.gz: f38ef6d9aef9f9d29ff14d9a091c60906438789b124334031bd102e605296fb8d844b5d2a2f97dd094e4586c4dd06a662c149d3f9d1ad6ad70cf5b5f4edf21d5
6
+ metadata.gz: 9113399868c37c29fc38521c88b1f4acd2477b6094469c97be54983eb46753245e79ea7d4a56cb641ade8746b05323ecdeef276321aeb70e19ceca123d232566
7
+ data.tar.gz: 1f101adea0a26968fe86fc9f7d5084e4cd821ab9b17ca528762bb55573a0be5eeb98e2eba0e5affa83da30168f046c6ea1b7e7cd6654beed0eeb599ea0aaaa73
@@ -5,7 +5,7 @@ $discussion-toolbar-color: #f6f6fa;
5
5
  $message-divider-color: #ecf0f1;
6
6
  $discussion-button-color: #fafafa;
7
7
  $toolbar-filter-color: #808080;
8
- $moderator-star-color: #dd9900;
8
+ $moderator-badge-color: #dd9900;
9
9
 
10
10
  .discussions-list {
11
11
  margin: 30px 0;
@@ -177,8 +177,17 @@ $moderator-star-color: #dd9900;
177
177
  width: 100%;
178
178
  }
179
179
 
180
- .moderator-star {
181
- color: $moderator-star-color;
180
+ .moderator-badge {
181
+ position: relative;
182
+ top: -2px;
183
+ margin: 2px;
184
+ font-size: 12px;
185
+ text-transform: uppercase;
186
+ color: white;
187
+ background-color: $moderator-badge-color;
188
+ border: solid $moderator-badge-color 1px;
189
+ border-radius: 5px;
190
+ padding-inline: 5px;
182
191
  }
183
192
 
184
193
  .discussion-user-menu {
@@ -326,12 +335,15 @@ summary.discussion-summary {
326
335
  .actions {
327
336
  float: right;
328
337
  a {
329
- margin-left: 5px;
338
+ margin-left: 20px;
330
339
  cursor: pointer;
331
340
  }
332
341
  .discussion-message-approved {
333
342
  text-decoration: none;
334
- &.selected {
343
+ i {
344
+ transition: color 0.3s;
345
+ }
346
+ &:hover, &.selected {
335
347
  i {
336
348
  color: $brand-success;
337
349
  }
@@ -341,17 +353,20 @@ summary.discussion-summary {
341
353
  text-decoration: none;
342
354
  i {
343
355
  position: relative;
356
+ transition: color 0.3s;
344
357
  &:after {
345
358
  position: absolute;
346
- left: 6px;
359
+ left: 10px;
360
+ top: -6px;
347
361
  content: ' ';
348
- height: 19px;
362
+ height: 30px;
349
363
  width: 2px;
350
364
  transform: rotate(-45deg);
351
365
  background-color: #aaaaaa;
366
+ transition: background-color 0.3s;
352
367
  }
353
368
  }
354
- &.selected {
369
+ &:hover, &.selected {
355
370
  i {
356
371
  color: $brand-primary;
357
372
  &:after {
@@ -360,6 +375,14 @@ summary.discussion-summary {
360
375
  }
361
376
  }
362
377
  }
378
+ .discussion-delete-message {
379
+ i {
380
+ transition: color 0.3s;
381
+ &:hover {
382
+ color: black;
383
+ }
384
+ }
385
+ }
363
386
  i {
364
387
  color: #aaaaaa
365
388
  }
@@ -34,8 +34,8 @@ class ApplicationController < ActionController::Base
34
34
  helper_method :current_workspace,
35
35
  :login_button,
36
36
  :notifications_count,
37
- :user_notifications_path,
38
37
  :has_notifications?,
38
+ :notifications,
39
39
  :subject,
40
40
  :should_choose_organization?,
41
41
  :current_immersive_organizations,
@@ -0,0 +1,26 @@
1
+ class ExamAuthorizationRequestsController < ApplicationController
2
+ def show
3
+ @authorization_request = ExamAuthorizationRequest.find(params[:id])
4
+ current_user.read_notification! @authorization_request
5
+ end
6
+
7
+ def create
8
+ authorization_request = ExamAuthorizationRequest.create! authorization_request_params
9
+ current_user.read_notification! authorization_request.exam_registration
10
+ flash.notice = I18n.t :exam_authorization_request_created
11
+ end
12
+
13
+ def update
14
+ ExamAuthorizationRequest.update params[:id], authorization_request_params
15
+ flash.notice = I18n.t :exam_authorization_request_saved
16
+ redirect_to root_path
17
+ end
18
+
19
+ private
20
+
21
+ def authorization_request_params
22
+ params
23
+ .require(:exam_authorization_request).permit(:exam_id, :exam_registration_id)
24
+ .merge(user: current_user, organization: Organization.current)
25
+ end
26
+ end
@@ -0,0 +1,6 @@
1
+ class ExamRegistrationsController < ApplicationController
2
+ def show
3
+ @registration = ExamRegistration.find(params[:id])
4
+ @authorization_request = @registration.authorization_request_for(current_user)
5
+ end
6
+ end
@@ -44,4 +44,8 @@ module ApplicationHelper
44
44
  <span class="#{'hidden' unless active} #{options[:class]}">#{active_text}</span>
45
45
  }.html_safe
46
46
  end
47
+
48
+ def notification_preview_for(target)
49
+ render "notifications/#{target.class.name.underscore}", { target: target }
50
+ end
47
51
  end
@@ -7,9 +7,9 @@ module IconsHelper
7
7
  fa_icon name, options.merge(class: 'fa-fw fixed-icon')
8
8
  end
9
9
 
10
- def exercise_status_icon(exercise)
11
- link_to exercise_status_fa_icon(exercise),
12
- exercise_path(exercise) if current_user?
10
+ def assignment_status_icon(assignment)
11
+ link_to contextualization_fa_icon(assignment),
12
+ exercise_path(assignment.exercise) if current_user?
13
13
  end
14
14
 
15
15
  def language_icon(language)
@@ -22,10 +22,6 @@ module IconsHelper
22
22
  fa_icon(*icon_for(contextualization))
23
23
  end
24
24
 
25
- def exercise_status_fa_icon(exercise)
26
- contextualization_fa_icon(exercise.assignment_for(current_user))
27
- end
28
-
29
25
  def discussion_status_fa_icon(discussion)
30
26
  contextualization_fa_icon(discussion)
31
27
  end
@@ -35,10 +31,6 @@ module IconsHelper
35
31
  [iconized[:type], class: "text-#{iconized[:class]} status-icon"]
36
32
  end
37
33
 
38
- def class_for_exercise(exercise)
39
- icon_class_for(exercise.assignment_for(current_user))
40
- end
41
-
42
34
  def icon_class_for(iconizable)
43
35
  iconizable.iconize[:class].to_s
44
36
  end
@@ -39,8 +39,8 @@ module MenuBarHelper
39
39
  li_tag menu_item('sign-out-alt', :sign_out, logout_path(origin: url_for))
40
40
  end
41
41
 
42
- def menu_item(icon, name, url)
43
- link_to fixed_fa_icon(icon, text: t(name)), url, role: 'menuitem', tabindex: '-1'
42
+ def menu_item(icon, name, url, translation_params = {})
43
+ link_to fixed_fa_icon(icon, text: t(name, translation_params)), url, role: 'menuitem', tabindex: '-1'
44
44
  end
45
45
 
46
46
  def any_menu_bar_links?
@@ -1,8 +1,8 @@
1
1
  module ProgressBarHelper
2
2
  include IconsHelper
3
3
 
4
- def class_for_progress_list_item(exercise, active)
5
- "progress-list-item text-center #{class_for_exercise(exercise)} #{active ? 'active' : ''}"
4
+ def class_for_progress_list_item(assignment, active)
5
+ "progress-list-item text-center #{icon_class_for(assignment)} #{active ? 'active' : ''}"
6
6
  end
7
7
 
8
8
  end
@@ -26,9 +26,9 @@
26
26
  <div>
27
27
  <h3><%= t(:lessons) %></h3>
28
28
 
29
- <% @chapter.lessons.each do |lesson| %>
29
+ <% @chapter.lessons.includes(guide: :exercises).each do |lesson| %>
30
30
  <h4><%= lesson.number %>. <%= link_to_path_element lesson, mode: :plain %></h4>
31
- <%= render partial: 'layouts/progress_listing', locals: { exercises: lesson.exercises } %>
31
+ <%= render partial: 'layouts/progress_listing', locals: { guide: lesson.guide } %>
32
32
  <% end %>
33
33
  </div>
34
34
  <% end %>
@@ -3,29 +3,29 @@
3
3
  <div class="discussion-message-bubble-header">
4
4
  <div class="discussion-message-bubble-title">
5
5
  <%= user.name %>
6
+ <% if user.moderator_here? %>
7
+ <span class="moderator-badge"><%= t(:moderation) %></span>
8
+ <% end %>
6
9
  <span class="message-date">
7
10
  <%= t(:time_since, time: time_ago_in_words(message.created_at)) %>
8
11
  </span>
9
- <% if user.moderator_here? %>
10
- <%= fa_icon(:star, 'data-toggle': 'tooltip', title: (t :moderator), class: 'moderator-star') %>
11
- <% end %>
12
12
  <span class="actions">
13
13
  <% if message.authorized? current_user %>
14
14
  <% if current_user&.moderator_here? %>
15
15
  <a class="discussion-message-approved <%= 'selected' if message.approved? %>" onclick="mumuki.Forum.discussionMessageToggleApprove('<%= approve_discussion_message_url(@discussion, message) %>', $(this))">
16
- <%= fa_icon(:check, class: 'fa-xs') %>
16
+ <%= fa_icon(:check, class: 'fa-lg') %>
17
17
  </a>
18
18
  <% if message.from_initiator? %>
19
19
  <a class="discussion-message-not-actually-a-question <%= 'selected' if message.not_actually_a_question? %>" onclick="mumuki.Forum.discussionMessageToggleNotActuallyAQuestion('<%= question_discussion_message_url(@discussion, message) %>', $(this))">
20
- <%= fa_icon('question-circle', type: 'regular', class: 'fa-xs') %>
20
+ <%= fa_icon('question-circle', type: 'regular', class: 'fa-lg') %>
21
21
  </a>
22
22
  <% end %>
23
23
  <% end %>
24
- <%= link_to fa_icon('trash-alt', type: :regular), discussion_message_path(@discussion, message), method: :delete, data: { confirm: t(:are_you_sure, action: t(:destroy_message)) } %>
24
+ <%= link_to fa_icon('trash-alt', type: :regular, class: 'fa-lg'), discussion_message_path(@discussion, message), method: :delete, data: { confirm: t(:are_you_sure, action: t(:destroy_message)) }, class: 'discussion-delete-message' %>
25
25
  <% end %>
26
26
  <% if should_show_approved_for?(current_user, message) %>
27
27
  <span class="discussion-message-approved selected">
28
- <%= fa_icon(:check, class: 'fa-xs', 'data-toggle': 'tooltip', title: (t :approved_message)) %>
28
+ <%= fa_icon(:check, class: 'fa-lg', 'data-toggle': 'tooltip', title: (t :approved_message)) %>
29
29
  </span>
30
30
  <% end %>
31
31
  </span>
@@ -0,0 +1,17 @@
1
+ <%= content_for :breadcrumbs do %>
2
+ <%= breadcrumbs @authorization_request %>
3
+ <% end %>
4
+
5
+ <div class="row">
6
+ <div class="mu-inline-block-left">
7
+ <h1>
8
+ <%= t :exam_registration_to, description: @authorization_request.exam_registration.description %>
9
+ </h1>
10
+ </div>
11
+ </div>
12
+
13
+ <% if @authorization_request.approved? %>
14
+ <%= t :exam_authorization_request_approved_html, date: l(@authorization_request.exam.start_time, format: :long) %>
15
+ <% else %>
16
+ <%= t :exam_authorization_request_rejected %>
17
+ <% end %>
@@ -0,0 +1,37 @@
1
+ <%= content_for :breadcrumbs do %>
2
+ <%= breadcrumbs @registration %>
3
+ <% end %>
4
+
5
+ <div class="row">
6
+ <div class="mu-inline-block-left">
7
+ <h1>
8
+ <%= t :exam_registration_to, description: @registration.description %>
9
+ </h1>
10
+ </div>
11
+ </div>
12
+
13
+ <div class="row">
14
+ <div class="mu-inline-block-left">
15
+ <div class="bs-callout bs-callout-info">
16
+ <h4 class="text-info">
17
+ <strong><%= fa_icon :info_circle %> <%= t :important_info %></strong>
18
+ </h4>
19
+ <p>
20
+ <%= t :exam_registration_explanation_html, date: l(@registration.end_time, format: :long) %>
21
+ </p>
22
+ </div>
23
+ <%= form_for @authorization_request do |f| %>
24
+ <%= f.hidden_field :exam_registration_id, value: @registration.id %>
25
+ <div class="form-group">
26
+ <%= f.label :exam_id, t(:exam_registration_choose_exam) %>
27
+ <% @registration.exams.each do |exam| %>
28
+ <div class="field radio complementary complementary-radio">
29
+ <%= f.radio_button(:exam_id, exam.id, id: exam.id, required: true, checked: @authorization_request.exam_id == exam.id) %>
30
+ <%= label_tag exam.id, l(exam.start_time, format: :long) %>
31
+ </div>
32
+ <% end %>
33
+ </div>
34
+ <button class="btn btn-success"> <%= t :save %> </button>
35
+ <% end %>
36
+ </div>
37
+ </div>
@@ -65,6 +65,6 @@
65
65
  <%= render partial: 'layouts/modals/level_up' %>
66
66
  <% end %>
67
67
 
68
- <%= render partial: 'layouts/modals/guide_corollary', locals: {guide: @guide} %>
68
+ <%= render partial: 'layouts/modals/guide_corollary', locals: {guide: @guide} if @stats.almost_done? %>
69
69
  <%= render partial: 'layouts/modals/new_message', locals: {exercise: @exercise} if should_render_message_input?(@exercise) %>
70
70
  <% end if current_user? %>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <%= render partial: 'layouts/authoring', locals: {guide: @guide} %>
4
4
 
5
- <% if subject.timed? && subject.started?(current_user) && !current_user.teacher? %>
5
+ <% if subject.timed? && @stats.started? && !current_user.teacher? %>
6
6
  <%= render partial: 'layouts/timer', locals: {end_time: subject.real_end_time(current_user)} %>
7
7
  <% end %>
8
8
 
@@ -10,10 +10,10 @@
10
10
 
11
11
  <h3>
12
12
  <%= t :exercises %>
13
- <%= restart_guide_link(@guide) if current_user && @guide.started?(current_user) && @guide.resettable? %>
13
+ <%= restart_guide_link(@guide) if current_user && @stats.started? && @guide.resettable? %>
14
14
  </h3>
15
15
 
16
- <%= render partial: 'layouts/progress_listing', locals: {exercises: @guide.exercises} %>
16
+ <%= render partial: 'layouts/progress_listing', locals: { guide: @guide } %>
17
17
 
18
18
  <% if @stats&.done? %>
19
19
  <div class="text-box">
@@ -1,12 +1,14 @@
1
1
  <div class="progress-list-flex">
2
- <% guide.exercises.each do |e| %>
2
+ <% assignments = guide.assignments_for(current_user) %>
3
+ <% assignments.each do |assignment| %>
4
+ <% exercise = assignment.exercise %>
3
5
  <a
4
- <%= turbolinks_enable_for e %>
5
- href="<%= exercise_path(e)%>"
6
- aria-label="<%= e.navigable_name %>"
7
- title="<%= e.navigable_name %>"
8
- data-mu-exercise-id="<%= e.id %>"
9
- class="<%= class_for_progress_list_item(e, e == actual)%>">
6
+ <%= turbolinks_enable_for exercise %>
7
+ href="<%= exercise_path(exercise)%>"
8
+ aria-label="<%= exercise.navigable_name %>"
9
+ title="<%= exercise.navigable_name %>"
10
+ data-mu-exercise-id="<%= exercise.id %>"
11
+ class="<%= class_for_progress_list_item(assignment, exercise == actual)%>">
10
12
  </a>
11
13
  <% end %>
12
14
  </div>
@@ -1,9 +1,9 @@
1
1
  <ul class="progress-listing">
2
- <% exercises.each do |exercise| %>
3
- <% cache [exercise, exercise.status_for(current_user), Organization.current] do %>
4
- <li <%= turbolinks_enable_for(exercise) %>>
5
- <%= exercise_status_icon exercise %>
6
- <%= link_to_path_element(exercise) %>
2
+ <% guide.assignments_for(current_user).each do |assignment| %>
3
+ <% cache [assignment.exercise, assignment.status, Organization.current] do %>
4
+ <li <%= turbolinks_enable_for(assignment.exercise) %>>
5
+ <%= assignment_status_icon assignment %>
6
+ <%= link_to_path_element(assignment.exercise) %>
7
7
  </li>
8
8
  <% end %>
9
9
  <% end %>
@@ -25,12 +25,7 @@
25
25
  <span class="mu-level-number"></span>
26
26
  </div>
27
27
  <% end %>
28
- <div class="dropdown mu-navbar-element notifications-box <%= 'notifications-box-empty' unless has_notifications? %>">
29
- <a href=<%= "#{user_notifications_path}" %>>
30
- <i class="fas fa-bell fa-fw fa-2x mu-navbar-icon"></i>
31
- <span class="badge badge-notifications"><%= notifications_count %></span>
32
- </a>
33
- </div>
28
+ <%= render partial: 'notifications/dropdown' %>
34
29
  <div class="dropdown mu-navbar-element">
35
30
  <div id="profileDropdown" class="profile-dropdown" data-toggle="dropdown" aria-label="<%= t(:user) %>" role="menu" tabindex="0">
36
31
  <%= profile_picture_for current_user %>
@@ -0,0 +1 @@
1
+ <%= menu_item :comments, :new_discussion_message, url_for([target.item, target]), { title: target.item.name.truncate_words(3) } %>
@@ -0,0 +1,13 @@
1
+ <div class="dropdown mu-navbar-element notifications-box <%= 'notifications-box-empty' unless has_notifications? %>">
2
+ <div id="notificationsDropdown" class="profile-dropdown" data-toggle="dropdown" aria-label="<%= t(:notifications) %>" role="menu" tabindex="0">
3
+ <i class="fas fa-bell fa-fw fa-2x mu-navbar-icon"></i>
4
+ <span class="badge badge-notifications"><%= notifications_count %></span>
5
+ </div>
6
+ <ul id="notificationsPanel" class="dropdown-menu dropdown-menu-right" aria-labelledby="notificationsDropdown">
7
+ <% notifications.each do |it| %>
8
+ <li>
9
+ <%= notification_preview_for it.target %>
10
+ </li>
11
+ <% end %>
12
+ </ul>
13
+ </div>
@@ -0,0 +1 @@
1
+ <%= menu_item :book_open, :exam_authorization_request_updated, url_for(target), { description: target.exam_registration.description } %>
@@ -0,0 +1 @@
1
+ <%= menu_item :book_open, :exam_registration_open, url_for(target), { description: target.description } %>
@@ -0,0 +1 @@
1
+ <%= menu_item :envelope, :new_message_received, url_for(target.exercise), { sender: target.sender } %>
data/config/routes.rb CHANGED
@@ -22,6 +22,9 @@ Rails.application.routes.draw do
22
22
  end
23
23
  end
24
24
 
25
+ resources :exam_registrations, only: [:show]
26
+ resources :exam_authorization_requests, only: [:show, :create, :update]
27
+
25
28
  resources :book, only: [:show]
26
29
  resources :chapters, only: [:show] do
27
30
  concerns :debatable, debatable_class: 'Chapter'