mumuki-laboratory 8.5.0 → 8.6.0

Sign up to get free protection for your applications and to get access to all the features.
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'