mumuki-laboratory 9.19.0 → 9.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/mumuki_laboratory/application/_layout.scss +0 -8
  3. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_discussion.scss +1 -3
  4. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_user_profile.scss +1 -9
  5. data/app/assets/stylesheets/mumuki_laboratory/application.scss +0 -1
  6. data/app/controllers/appendixes_controller.rb +7 -0
  7. data/app/controllers/application_controller.rb +6 -2
  8. data/app/controllers/complements_controller.rb +13 -0
  9. data/app/helpers/application_helper.rb +3 -1
  10. data/app/helpers/discussions_helper.rb +41 -26
  11. data/app/helpers/profile_helper.rb +1 -1
  12. data/app/views/book/show.html.erb +21 -18
  13. data/app/views/chapters/show.html.erb +1 -1
  14. data/app/views/discussions/_description_message.html.erb +6 -4
  15. data/app/views/discussions/_message.html.erb +10 -8
  16. data/app/views/discussions/show.html.erb +1 -1
  17. data/app/views/errors/forbidden.html.erb +1 -3
  18. data/app/views/errors/gone.html.erb +1 -2
  19. data/app/views/errors/not_found.html.erb +1 -1
  20. data/app/views/guides/_guide.html.erb +9 -5
  21. data/app/views/layouts/_discussions.html.erb +1 -3
  22. data/app/views/users/_basic_profile_fields.html.erb +20 -16
  23. data/app/views/users/_edit_user_form.html.erb +9 -7
  24. data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -6
  25. data/lib/mumuki/laboratory/controllers/validate_access_mode.rb +5 -1
  26. data/lib/mumuki/laboratory/locales/es-CL.yml +1 -1
  27. data/lib/mumuki/laboratory/locales/es.yml +1 -1
  28. data/lib/mumuki/laboratory/version.rb +1 -1
  29. data/spec/controllers/discussions_messages_controller_spec.rb +5 -5
  30. data/spec/controllers/messages_controller_spec.rb +3 -3
  31. data/spec/dummy/db/schema.rb +4 -1
  32. data/spec/features/discussion_flow_spec.rb +2 -2
  33. data/spec/features/guides_flow_spec.rb +29 -7
  34. data/spec/features/immersive_redirection_spec.rb +1 -1
  35. data/spec/features/not_found_public_flow_spec.rb +8 -1
  36. data/spec/features/profile_flow_spec.rb +3 -1
  37. data/spec/features/read_only_flow_spec.rb +72 -1
  38. metadata +107 -108
  39. data/app/assets/stylesheets/mumuki_laboratory/application/_alerts.scss +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74909716a7b8701adb5f410a2fded18a19779547b107635965361cf2247c7595
4
- data.tar.gz: ac32359e5add8298400567f10d1d1fcf191db92eeacaa258d33387b6d9433de9
3
+ metadata.gz: 132cadbd6244acaece507d8f75d1613daf1c4f9f682b1b929d5c6468102a596b
4
+ data.tar.gz: 45ce5c37552fe64a9766ac4548f944f045f843177b843706277ca56f42125dbd
5
5
  SHA512:
6
- metadata.gz: 33cac73574865ca539d574765c2b85c57da593b11078e92f2c17cd47b51382f33840083458c396893ac3ed0227f068fdfab9e1fc83ab7556186ca901c2c6ba50
7
- data.tar.gz: dae03d18dfd66681d899d3546bc98aa36b88e1d85947202f032f1a7972c8cc3adb33e4cda12795a46719445530fb1aeff4d2783cae4234836f234f163a45cacf
6
+ metadata.gz: 9c6046c3ce7394386bc2f43d3e063c7eae999a70d96fde67243809e1e107d4f35638333cdf87e9af9aa5f3ae797585c82bd69a1ba293511842eb9bc4608c4518
7
+ data.tar.gz: fdeaa6997d54fb7dd6a75224c25a3a40f0d2e05414b934ff1fc3e5f60568c2de6ac32b39c6bcbb0f5b6e09cffc3b53b51e6cb8264e3ade7ee1060ccd05ae4252
@@ -34,14 +34,6 @@
34
34
  background-image: none;
35
35
  }
36
36
 
37
- .alert {
38
- margin: 10px 50px 10px 15px;
39
- }
40
-
41
- .alert-danger a {
42
- color: greenyellow;
43
- }
44
-
45
37
  .exercise-search-box {
46
38
  width: 263px;
47
39
  position: fixed
@@ -318,7 +318,6 @@ $moderator-badge-color: #dd9900;
318
318
  margin-bottom: 20px;
319
319
  .discussion-message-bubble-header {
320
320
  background-color: $mu-color-highlight-background;
321
- height: 40px;
322
321
  &:before {
323
322
  position: absolute;
324
323
  top: calc(20px - #{$discussion-message-arrow-size + 2px} / 2);
@@ -330,12 +329,11 @@ $moderator-badge-color: #dd9900;
330
329
  }
331
330
  .discussion-message-bubble-title {
332
331
  padding: 5px 15px;
333
- display: block;
332
+ display: flex;
334
333
  .message-date {
335
334
  font-size: 15px;
336
335
  }
337
336
  .actions {
338
- float: right;
339
337
  > a, .dropdown {
340
338
  margin-left: 20px;
341
339
  cursor: pointer;
@@ -7,6 +7,7 @@
7
7
 
8
8
  .mu-profile-actions {
9
9
  margin-left: auto;
10
+ text-align: center;
10
11
 
11
12
  .btn {
12
13
  width: 150px;
@@ -18,15 +19,6 @@
18
19
  border-color: lighten($mu-color-complementary, 15%);
19
20
  }
20
21
  }
21
-
22
- &.mobile {
23
- text-align: center;
24
- margin: 20px 5px -20px -10px;
25
-
26
- .btn {
27
- width: calc(50% - 20px);
28
- }
29
- }
30
22
  }
31
23
 
32
24
  .mu-profile-info {
@@ -13,5 +13,4 @@ $da-font-path: asset-path('assets');
13
13
  @import "application/invitations";
14
14
  @import "application/modules";
15
15
  @import "application/errors";
16
- @import "application/alerts";
17
16
  @import "application/codemirror-themes";
@@ -1,5 +1,12 @@
1
1
  class AppendixesController < ApplicationController
2
+
2
3
  def show
3
4
  @chapter = Chapter.find(params[:chapter_id])
4
5
  end
6
+
7
+ private
8
+
9
+ def authorization_minimum_role
10
+ :ex_student
11
+ end
5
12
  end
@@ -44,7 +44,8 @@ class ApplicationController < ActionController::Base
44
44
  :theme_stylesheet_url,
45
45
  :extension_javascript_url,
46
46
  :current_immersive_path,
47
- :current_access_mode
47
+ :current_access_mode,
48
+ :limited_query?
48
49
 
49
50
  add_flash_types :info
50
51
 
@@ -129,7 +130,6 @@ class ApplicationController < ActionController::Base
129
130
  def validate_user_profile!
130
131
  unless current_user.profile_completed?
131
132
  save_location_before! :profile_completion
132
- flash[:info] = I18n.t :please_fill_profile_data
133
133
  redirect_to edit_user_path
134
134
  end
135
135
  end
@@ -171,4 +171,8 @@ class ApplicationController < ActionController::Base
171
171
  current_user.restore_organization_progress!(Organization.current)
172
172
  end
173
173
  end
174
+
175
+ def limited_query?
176
+ params[:limit].present?
177
+ end
174
178
  end
@@ -1,8 +1,21 @@
1
1
  class ComplementsController < GuideContainerController
2
+ include Mumuki::Laboratory::Controllers::ValidateAccessMode
2
3
 
3
4
  private
4
5
 
5
6
  def subject
6
7
  @complement ||= Complement.find_by(id: params[:id])
7
8
  end
9
+
10
+ def authorization_minimum_role
11
+ :ex_student
12
+ end
13
+
14
+ def subject_container
15
+ subject.guide
16
+ end
17
+
18
+ def contentless_subject?
19
+ subject_container.exercises.empty?
20
+ end
8
21
  end
@@ -17,7 +17,9 @@ module ApplicationHelper
17
17
  end
18
18
 
19
19
  def paginate(object, options = {})
20
- super(object, {theme: 'bootstrap-5', pagination_class: 'flex-wrap justify-content-center'}.merge(options))
20
+ unless limited_query?
21
+ super(object, {theme: 'bootstrap-5', pagination_class: 'flex-wrap justify-content-center'}.merge(options))
22
+ end
21
23
  end
22
24
 
23
25
  def last_box_class(trailing_boxes)
@@ -38,14 +38,14 @@ module DiscussionsHelper
38
38
 
39
39
  def solve_discussion_params_for(user)
40
40
  if user&.moderator_here?
41
- {status: :pending_review, sort: :responses_count_asc, requires_moderator_response: true}
41
+ {status: :pending_review, sort: :responses_count_asc, requires_attention: true}
42
42
  else
43
43
  {status: :opened, sort: :responses_count_desc}
44
44
  end
45
45
  end
46
46
 
47
47
  def default_discussions_params
48
- {status: :solved, sort: :upvotes_count_desc}
48
+ {status: :solved, sort: :created_at_desc, recent: true, limit: 15}
49
49
  end
50
50
 
51
51
  def user_avatar(user, image_class='')
@@ -53,37 +53,37 @@ module DiscussionsHelper
53
53
  end
54
54
 
55
55
  def forum_terms_link
56
- %Q{
56
+ <<~HTML.html_safe
57
57
  <span>
58
58
  #{ t(:forum_terms_link, terms_link: link_to_forum_terms).html_safe }
59
59
  </span>
60
- }.html_safe
60
+ HTML
61
61
  end
62
62
 
63
63
  def discussion_messages_count(discussion)
64
- %Q{
64
+ <<~HTML.html_safe
65
65
  <span class="discussion-messages-count">
66
66
  #{fa_icon :comments, type: :regular, text: discussion.messages_count}
67
67
  </span>
68
- }.html_safe
68
+ HTML
69
69
  end
70
70
 
71
71
  def discussion_validated_messages_count(discussion)
72
- %Q{
72
+ <<~HTML.html_safe
73
73
  <span class="discussion-validated-messages-count">
74
74
  #{fa_icon :comment, type: :regular}#{fa_icon :check, text: discussion.validated_messages_count}
75
75
  </span>
76
- }.html_safe
76
+ HTML
77
77
  end
78
78
 
79
79
  def discussion_upvotes_icon(discussion)
80
80
  if discussion.upvotes_count > 0
81
- %Q{
81
+ <<~HTML.html_safe
82
82
  <span class="discussion-icon fa-stack fa-xs">
83
83
  <i class="far fa-star fa-stack-2x"></i>
84
84
  <i class="fas fa-stack-1x">#{discussion.upvotes_count}</i>
85
85
  </span>
86
- }.html_safe
86
+ HTML
87
87
  end
88
88
  end
89
89
 
@@ -99,16 +99,21 @@ module DiscussionsHelper
99
99
  end
100
100
 
101
101
  def new_discussion_link(teaser_text, link_text)
102
- %Q{
102
+ <<~HTML.html_safe
103
103
  <h4>
104
104
  <span>#{t(teaser_text)}</span>
105
105
  #{link_to t(link_text), new_exercise_discussion_path(@debatable, anchor: 'new-discussion-description-container') }
106
106
  </h4>
107
- }.html_safe
107
+ HTML
108
108
  end
109
109
 
110
- def discussion_count_for_status(status, discussions)
111
- discussions.scoped_query_by(discussion_filter_params, excluded_params: [:status], excluded_methods: [:page]).by_status(status).count
110
+ def discussion_status_counts(discussions)
111
+ discussions.scoped_query_by(discussion_filter_params, excluded_params: [:status], excluded_methods: [:page])
112
+ .group(:status)
113
+ .reorder('')
114
+ .pluck(:status, 'count(*)')
115
+ .to_h
116
+ .transform_keys(&:to_sym)
112
117
  end
113
118
 
114
119
  def discussions_reset_query_link
@@ -122,11 +127,21 @@ module DiscussionsHelper
122
127
  #TODO: this one uses a long method chain in order to take advantage of eager load
123
128
  # Delegate it once again when polymorphic association is removed
124
129
  def discussions_languages(discussions)
125
- @languages ||= discussions.map { |it| it.exercise.language.name }.uniq
130
+ @languages ||= discussions.distinct
131
+ .joins(:exercise)
132
+ .pluck('languages.name')
126
133
  end
127
134
 
128
- def discussion_status_filter_link(status, discussions)
129
- discussions_count = discussion_count_for_status(status, discussions)
135
+ def discussion_status_filter_links(discussions)
136
+ status_counts = discussion_status_counts(discussions)
137
+
138
+ discussions_statuses.map do |status|
139
+ discussion_status_filter_link(status, status_counts)
140
+ end.compact.join("\n").html_safe
141
+ end
142
+
143
+ def discussion_status_filter_link(status, status_counts)
144
+ discussions_count = status_counts[status.to_sym] || 0
130
145
  if status.should_be_shown?(discussions_count, current_user)
131
146
  discussion_filter_item(:status, status) do
132
147
  discussion_status_filter(status, discussions_count)
@@ -135,17 +150,17 @@ module DiscussionsHelper
135
150
  end
136
151
 
137
152
  def discussion_status_filter(status, discussions_count)
138
- %Q{
139
- #{discussion_status_fa_icon(status)}
153
+ <<~HTML.html_safe
154
+ #{discussion_status_fa_icon(status)}
140
155
  <span>
141
156
  #{t("#{status}_count", count: discussions_count)}
142
157
  </span>
143
- }.html_safe
158
+ HTML
144
159
  end
145
160
 
146
161
  def discussion_dropdown_filter(label, filters, can_select_all = false, &block)
147
162
  if filters.present?
148
- %Q{
163
+ <<~HTML.html_safe
149
164
  <div class="dropdown discussions-toolbar-filter">
150
165
  <a id="dropdown-#{label}" data-bs-toggle="dropdown" role="menu">
151
166
  #{t label} #{fa_icon :'caret-down', class: 'fa-xs'}
@@ -155,7 +170,7 @@ module DiscussionsHelper
155
170
  #{discussion_filter_list(label, filters, &block)}
156
171
  </ul>
157
172
  </div>
158
- }.html_safe
173
+ HTML
159
174
  end
160
175
  end
161
176
 
@@ -244,7 +259,7 @@ module DiscussionsHelper
244
259
  end
245
260
 
246
261
  def discussion_delete_message_dropdown(discussion, message)
247
- %Q{
262
+ <<~HTML.html_safe
248
263
  <span class="dropdown">
249
264
  #{content_tag :span, fa_icon('trash-alt', type: :regular, class: 'fa-lg'), role: 'menu', 'data-bs-toggle': 'dropdown',
250
265
  class: 'discussion-delete-message', id: 'deleteDiscussionDropdown'}
@@ -254,17 +269,17 @@ module DiscussionsHelper
254
269
  #{discussion_delete_message_option discussion, message, :discloses_personal_information, 'user-tag'}
255
270
  </ul>
256
271
  </span>
257
- }.html_safe
272
+ HTML
258
273
  end
259
274
 
260
275
  def discussion_delete_message_option(discussion, message, motive, icon)
261
- %Q{
276
+ <<~HTML.html_safe
262
277
  <li>
263
278
  #{link_to fa_icon(icon, text: t("deletion_motive.#{motive}.present"), class: 'fa-fw fixed-icon'),
264
279
  discussion_message_path(discussion, message, motive: motive), method: :delete, class: 'dropdown-item',
265
280
  role: 'menuitem', data: { confirm: t(:are_you_sure, action: t(:destroy_message)) } }
266
281
  </li>
267
- }.html_safe
282
+ HTML
268
283
  end
269
284
 
270
285
  def message_deleted_text(message)
@@ -4,7 +4,7 @@ module ProfileHelper
4
4
  end
5
5
 
6
6
  def cancel_edit_profile_button
7
- link_to t(:cancel), :user, class: 'btn btn-secondary' if current_user.profile_completed?
7
+ link_to t(:cancel), :user, class: 'btn btn-outline-complementary' if current_user.profile_completed?
8
8
  end
9
9
 
10
10
  def save_edit_profile_button(form)
@@ -17,30 +17,33 @@
17
17
  <% end %>
18
18
  <% end %>
19
19
 
20
- <% if show_content?(@book) %>
21
- <h2><%= t(:chapters) %></h2>
22
- <% end %>
20
+ <% unless @book.chapters.empty? %>
21
+ <% if show_content?(@book) %>
22
+ <h2><%= t(:chapters) %></h2>
23
+ <% end %>
23
24
 
24
- <% @book.chapter_visibilities_in(current_workspace).each do |it, enabled| %>
25
+ <% @book.chapter_visibilities_in(current_workspace).each do |it, enabled| %>
25
26
 
26
- <% next unless show_content?(it.topic) %>
27
+ <% next unless show_content?(it.topic) %>
27
28
 
28
- <div class="chapter-container">
29
- <div class="chapter <%= enabled ? '' : 'mu-locked' %>">
30
- <h3><%= it.number %>. <%= link_to_path_element it, mode: :plain %></h3>
31
- <div class="text-box" <%= 'aria-label=""' unless enabled %>>
32
- <%= it.description_teaser_html %>
29
+ <div class="chapter-container">
30
+ <div class="chapter <%= enabled ? '' : 'mu-locked' %>">
31
+ <h3><%= it.number %>. <%= link_to_path_element it, mode: :plain %></h3>
32
+ <div class="text-box" <%= 'aria-label=""' unless enabled %>>
33
+ <%= it.description_teaser_html %>
34
+ </div>
33
35
  </div>
34
- </div>
35
36
 
36
- <% unless enabled %>
37
- <div class="text-center mu-lock">
38
- <i class="fas fa-lock fa-5x"></i>
39
- <p><%= t :locked_content %></p>
40
- </div>
41
- <% end %>
42
- </div>
37
+ <% unless enabled %>
38
+ <div class="text-center mu-lock">
39
+ <i class="fas fa-lock fa-5x"></i>
40
+ <p><%= t :locked_content %></p>
41
+ </div>
42
+ <% end %>
43
+ </div>
44
+ <% end %>
43
45
  <% end %>
46
+
44
47
  <% if current_user? && @exams.present? %>
45
48
  <h2><%= t(:exams) %></h2>
46
49
  <% @exams.each_with_index do |it, index| %>
@@ -20,7 +20,7 @@
20
20
  </div>
21
21
  </div>
22
22
 
23
- <% if @chapter.lessons.present? %>
23
+ <% unless @chapter.lessons.empty? %>
24
24
  <% if @chapter.monolesson? %>
25
25
  <div> <%= render partial: 'guides/guide', locals: { subject: @monolesson } %> </div>
26
26
  <% else %>
@@ -2,10 +2,12 @@
2
2
  <div class="discussion-message-bubble">
3
3
  <div class="discussion-message-bubble-header">
4
4
  <div class="discussion-message-bubble-title">
5
- <%= linked_discussion_user_name(discussion.initiator) %>
6
- <span class="message-date">
7
- <%= friendly_time(discussion.created_at, :time_since) %>
8
- </span>
5
+ <div class="flex-fill">
6
+ <%= linked_discussion_user_name(discussion.initiator) %>
7
+ <span class="message-date">
8
+ <%= friendly_time(discussion.created_at, :time_since) %>
9
+ </span>
10
+ </div>
9
11
  </div>
10
12
  </div>
11
13
  <div class="discussion-message-bubble-content">
@@ -2,14 +2,16 @@
2
2
  <div class="discussion-message-bubble">
3
3
  <div class="discussion-message-bubble-header">
4
4
  <div class="discussion-message-bubble-title">
5
- <%= linked_discussion_user_name user %>
6
- <% if user.moderator_here? %>
7
- <span class="moderator-badge"><%= t(:moderation) %></span>
8
- <% end %>
9
- <span class="message-date">
10
- <%= friendly_time(message.created_at, :time_since) %>
11
- </span>
12
- <span class="actions">
5
+ <div class="flex-fill">
6
+ <%= linked_discussion_user_name user %>
7
+ <% if message.from_moderator? %>
8
+ <span class="moderator-badge"><%= t(:moderation) %></span>
9
+ <% end %>
10
+ <span class="message-date">
11
+ <%= friendly_time(message.created_at, :time_since) %>
12
+ </span>
13
+ </div>
14
+ <span class="actions flex-shrink-0">
13
15
  <% if message.authorized?(current_user) && !message.deleted? %>
14
16
  <% if current_user&.moderator_here? %>
15
17
  <a class="discussion-message-approved <%= 'selected' if message.approved? %>"
@@ -26,7 +26,7 @@
26
26
  <%= render partial: 'discussions/description_message', locals: { discussion: @discussion } %>
27
27
  <% end %>
28
28
  <% @discussion.visible_messages.each do |message| %>
29
- <%= render partial: 'discussions/message', locals: { user: message.sender_user, message: message } %>
29
+ <%= render partial: 'discussions/message', locals: { user: message.sender, message: message } %>
30
30
  <% end %>
31
31
  <% if @discussion.commentable_by?(current_user) %>
32
32
  <hr class="message-divider">
@@ -8,12 +8,10 @@
8
8
  <%= t(:error_description, error: link_to_status_codes(403)).html_safe %>
9
9
  </p>
10
10
  <p class="mu-error-explanation-line">
11
- <%= Organization.current.explain_error(403, explanation).html_safe %>
11
+ <%= Organization.current.explain_error(error_code, explanation).html_safe %>
12
12
  </p>
13
13
  <p class="mu-error-contact-line mu-maybe">
14
14
  <%= t(:contact_administrator, link: mail_to_administrator).html_safe %>
15
15
  </p>
16
16
  </div>
17
17
  <% end %>
18
-
19
-
@@ -8,8 +8,7 @@
8
8
  <%= t(:error_description, error: link_to_status_codes(410)).html_safe %>
9
9
  </p>
10
10
  <p class="mu-error-explanation-line">
11
- <%= Organization.current.explain_error(410, explanation).html_safe %>
11
+ <%= Organization.current.explain_error(error_code, explanation).html_safe %>
12
12
  </p>
13
13
  </div>
14
14
  <% end %>
15
-
@@ -8,7 +8,7 @@
8
8
  <%= t(:error_description, error: link_to_error_404).html_safe %>
9
9
  </p>
10
10
  <p class="mu-error-explanation-line">
11
- <%= Organization.current.explain_error(404, :not_found_explanation).html_safe %>
11
+ <%= Organization.current.explain_error(:not_found, :not_found_explanation).html_safe %>
12
12
  </p>
13
13
  </div>
14
14
  <% end %>
@@ -8,12 +8,16 @@
8
8
 
9
9
  <%= yield if block_given? %>
10
10
 
11
- <h3>
12
- <%= t :exercises %>
13
- <%= restart_guide_link(@guide) if current_user && @stats.started? && @guide.resettable? %>
14
- </h3>
11
+ <% unless @guide.exercises.empty? %>
15
12
 
16
- <%= render partial: 'layouts/progress_listing', locals: { guide: @guide } %>
13
+ <h3>
14
+ <%= t :exercises %>
15
+ <%= restart_guide_link(@guide) if current_user && @stats.started? && @guide.resettable? %>
16
+ </h3>
17
+
18
+ <%= render partial: 'layouts/progress_listing', locals: { guide: @guide } %>
19
+
20
+ <% end %>
17
21
 
18
22
  <% if @stats&.done? %>
19
23
  <div class="text-box">
@@ -3,9 +3,7 @@
3
3
  <div class="discussions-toolbar">
4
4
  <div class="discussions-toolbar-status">
5
5
  <div class="d-none d-lg-block">
6
- <% discussions_statuses.each do |status| %>
7
- <%= discussion_status_filter_link(status, @discussions) %>
8
- <% end %>
6
+ <%= discussion_status_filter_links(@discussions) %>
9
7
  </div>
10
8
  </div>
11
9
  <div>
@@ -1,19 +1,23 @@
1
- <fieldset>
2
- <div><%= form.label(t :first_name) %></div>
3
- <div><%= form.text_field :first_name, required: true, class: 'form-control' %></div>
4
- </fieldset>
5
- <fieldset>
6
- <div><%= form.label(t :last_name) %></div>
7
- <div><%= form.text_field :last_name, required: true, class: 'form-control' %></div>
8
- </fieldset>
9
- <fieldset>
10
- <div><%= form.label(t :gender) %></div>
11
- <div><%= form.select :gender, options_for_select(User.genders.map { |gender, _| [t(gender), gender] }, @user.gender), {}, required: true, class: 'form-control form-select' %></div>
12
- </fieldset>
13
- <fieldset>
14
- <div><%= form.label(t :birthdate) %></div>
15
- <div><%= form.date_field :birthdate, min: Date.new(1900), max: 3.years.ago.end_of_year, required: true, class: 'form-control' %></div>
16
- </fieldset>
1
+ <div class="row">
2
+ <fieldset class="col-lg-6">
3
+ <div><%= form.label(t :first_name) %></div>
4
+ <div><%= form.text_field :first_name, required: true, class: 'form-control' %></div>
5
+ </fieldset>
6
+ <fieldset class="col-lg-6">
7
+ <div><%= form.label(t :last_name) %></div>
8
+ <div><%= form.text_field :last_name, required: true, class: 'form-control' %></div>
9
+ </fieldset>
10
+ </div>
11
+ <div class="row">
12
+ <fieldset class="col-lg-4">
13
+ <div><%= form.label(t :birthdate) %></div>
14
+ <div><%= form.date_field :birthdate, min: Date.new(1900), max: 3.years.ago.end_of_year, required: true, class: 'form-control' %></div>
15
+ </fieldset>
16
+ <fieldset class="col-lg-4 offset-lg-2">
17
+ <div><%= form.label(t :gender) %></div>
18
+ <div><%= form.select :gender, options_for_select(User.genders.map { |gender, _| [t(gender), gender] }, @user.gender), {}, required: true, class: 'form-control form-select' %></div>
19
+ </fieldset>
20
+ </div>
17
21
  <fieldset>
18
22
  <div><%= form.label(t :email) %></div>
19
23
  <div><%= form.text_field :email, readonly: true, class: 'form-control' %></div>
@@ -1,22 +1,24 @@
1
1
  <%= form_for :user, url: user_path, :html => { id: 'mu-user-form', class: 'mu-form' }, method: :put do |f| %>
2
2
  <div class="mu-user-header">
3
3
  <h1><%= t(:edit_profile) %></h1>
4
- <div class="mu-profile-actions d-none d-md-block">
5
- <%= cancel_edit_profile_button %>
6
- <%= save_edit_profile_button f %>
7
- </div>
8
4
  </div>
9
5
  <div class="row mu-tab-body">
10
- <div class="col-md-6 col-lg-4 mu-user-avatar-container">
6
+ <div class="col-md-6 col-lg-4 mu-user-avatar-container mb-3">
11
7
  <%= profile_picture_for(@user, id: 'mu-user-avatar', class: 'mu-user-avatar') %>
12
8
  <button class="fas fa-pencil-alt fa-2x btn mu-edit-avatar" type="button" data-bs-toggle="modal" data-bs-target="#mu-avatar-picker" tabindex="0"></button>
13
9
  </div>
14
- <div class="col-md-6 col-lg-8">
10
+ <div class="col-md-6 col-lg-8 mb-4">
11
+ <% unless current_user.profile_completed? %>
12
+ <div class="alert alert-info alert-dismissible fade show" role="alert">
13
+ <%= fa_icon 'info-circle', class: 'fa-lg me-2 align-middle', text: t(:please_fill_profile_data) %>
14
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
15
+ </div>
16
+ <% end %>
15
17
  <%= render partial: 'profile_fields', locals: {form: f} %>
16
18
  <%= render partial: 'layouts/terms_acceptance_disclaimer', locals: {user: @user} %>
17
19
  </div>
18
20
  </div>
19
- <div class="mu-profile-actions mobile d-block d-md-none">
21
+ <div class="mu-profile-actions">
20
22
  <%= cancel_edit_profile_button %>
21
23
  <%= save_edit_profile_button f %>
22
24
  </div>
@@ -41,27 +41,27 @@ module Mumuki::Laboratory::Controllers::DynamicErrors
41
41
  def forbidden
42
42
  message = "The operation on organization #{Organization.current} was forbidden to user #{current_user.uid} with permissions #{current_user.permissions}"
43
43
  Rails.logger.info message
44
- render_error 'forbidden', 403, locals: { explanation: :forbidden_explanation }, error_message: message
44
+ render_error 'forbidden', 403, locals: { error_code: :forbidden, explanation: :forbidden_explanation }, error_message: message
45
45
  end
46
46
 
47
47
  def disabled
48
- render_error 'forbidden', 403, locals: { explanation: :disabled_explanation }
48
+ render_error 'forbidden', 403, locals: { error_code: :disabled, explanation: :disabled_explanation }
49
49
  end
50
50
 
51
51
  def blocked_forum
52
- render_error 'forbidden', 403, locals: { explanation: :blocked_forum_explanation }
52
+ render_error 'forbidden', 403, locals: { error_code: :blocked_forum, explanation: :blocked_forum_explanation }
53
53
  end
54
54
 
55
55
  def gone
56
- render_error 'gone', 410, locals: { explanation: :gone_explanation }
56
+ render_error 'gone', 410, locals: { error_code: :gone, explanation: :gone_explanation }
57
57
  end
58
58
 
59
59
  def unprepared_organization
60
- render_error 'forbidden', 403, locals: { explanation: :unprepared_organization_explanation }
60
+ render_error 'forbidden', 403, locals: { error_code: :unprepared_organization, explanation: :unprepared_organization_explanation }
61
61
  end
62
62
 
63
63
  def disabled_organization
64
- render_error 'gone', 410, locals: { explanation: :disabled_organization_explanation }
64
+ render_error 'gone', 410, locals: { error_code: :disabled_organization, explanation: :disabled_organization_explanation }
65
65
  end
66
66
 
67
67
  def render_error(template, status, options={})