diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index c61f03c9ef..a657eb57c9 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -58,7 +58,7 @@ def create_or_update "locking" => @stale_answer ? render_to_string(partial: 'answers/locking', locals: { question: @question, answer: @stale_answer, user: @answer.user }, formats: [:html]) : nil, - "form" => render_to_string(partial: 'answers/new_edit', locals: { question: @question, answer: @answer, readonly: false }, formats: [:html]), + "form" => render_to_string(partial: 'answers/new_edit', locals: { question: @question, answer: @answer, readonly: false, locking: false }, formats: [:html]), "answer_status" => render_to_string(partial: 'answers/status', locals: { answer: @answer}, formats: [:html]) }, "section" => { diff --git a/app/views/answers/_locking.html.erb b/app/views/answers/_locking.html.erb index 1a34b49c36..e47642b96c 100644 --- a/app/views/answers/_locking.html.erb +++ b/app/views/answers/_locking.html.erb @@ -1,5 +1,5 @@

<%= _('The following answer cannot be saved') %>

- <%= render partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: true } %> + <%= render partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: true, locking: true } %>

<%= _('since %{name} saved the answer below while you were editing. Please, combine your changes and then save the answer again.') % { name: user.name} %>

\ No newline at end of file diff --git a/app/views/answers/_new_edit.html.erb b/app/views/answers/_new_edit.html.erb index 9968de84b9..caccfc1b43 100644 --- a/app/views/answers/_new_edit.html.erb +++ b/app/views/answers/_new_edit.html.erb @@ -1,4 +1,4 @@ -<%# locals: { question, answer, readonly } %> +<%# locals: { question, answer, readonly, locking } %> @@ -8,6 +8,16 @@ <%= f.hidden_field :question_id %> <%= f.hidden_field :lock_version %> <% end %> +
> + <% if question.option_based? %> + <%= render(partial: 'questions/new_edit_question_option_based', locals: { f: f, question: question, answer: answer }) %> + <% elsif question.question_format.textfield?%> + <%= render(partial: 'questions/new_edit_question_textfield', locals: { f: f, question: question, answer: answer }) %> + <% elsif question.question_format.textarea? %> + <%= render(partial: 'questions/new_edit_question_textarea', locals: { f: f, question: question, answer: answer, locking: locking }) %> + <% end %> + <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> +
<% annotation = question.first_example_answer %> <% if annotation.present? && annotation.text.present? %> @@ -20,14 +30,4 @@ <% end %> -
> - <% if question.option_based? %> - <%= render(partial: 'questions/new_edit_question_option_based', locals: { f: f, question: question, answer: answer }) %> - <% elsif question.question_format.textfield?%> - <%= render(partial: 'questions/new_edit_question_textfield', locals: { f: f, question: question, answer: answer }) %> - <% elsif question.question_format.textarea? %> - <%= render(partial: 'questions/new_edit_question_textarea', locals: { f: f, question: question, answer: answer }) %> - <% end %> - <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> -
<% end %> \ No newline at end of file diff --git a/app/views/answers/_status.html.erb b/app/views/answers/_status.html.erb index 3d3dcc1cf9..bddd28fb96 100644 --- a/app/views/answers/_status.html.erb +++ b/app/views/answers/_status.html.erb @@ -1,12 +1,10 @@ <%# locals: { answer } %> - - - + + + <% if answer.is_valid? %> -

+ <%= _('Answered')%> <%= _(' by %{user_name}') %{ :user_name => answer.user.name } if answer.user.present? %> -

+ <% end %> \ No newline at end of file diff --git a/app/views/guidance_groups/_show.html.erb b/app/views/guidance_groups/_show.html.erb index 6c9c6dec63..58c3c2fc20 100644 --- a/app/views/guidance_groups/_show.html.erb +++ b/app/views/guidance_groups/_show.html.erb @@ -11,26 +11,25 @@ <% end %> <% group.keys.each_with_index do |theme, i| %>
- - -
-
- <% group[theme].each do |guidance| %> - <%= raw guidance %> - <% end %> +
+ <% group[theme].each do |guidance| %> + <%= raw guidance %> + <% end %> +
<% end %> diff --git a/app/views/notes/_layout.html.erb b/app/views/notes/_layout.html.erb index 93c1f62534..e303546d84 100644 --- a/app/views/notes/_layout.html.erb +++ b/app/views/notes/_layout.html.erb @@ -1,23 +1,27 @@ <%# locals: { plan, question, answer } %> <% notes = answer.non_archived_notes %> -
-
- <%= render partial: "/notes/list", locals: { question_id: question.id, notes: notes, plan: plan }, formats: [:html] %> -
-
-<% if plan.commentable_by?(current_user) %> -
-
-
" data-question-id="<%= question.id %>"> - <%= render partial: "/notes/new", locals: { question: question, answer: answer, plan: plan }, formats: [:html] %> +
+
+
+
+ <%= render partial: "/notes/list", locals: { question_id: question.id, notes: notes, plan: plan }, formats: [:html] %>
-
-
-
-
- <%= link_to(_('Add Comment'), "#note_new#{question.id}", class: "btn btn-default note_new_link", role: "button", style: "visibility: hidden") %> + <% if plan.commentable_by?(current_user) %> +
+
+
" data-question-id="<%= question.id %>"> + <%= render partial: "/notes/new", locals: { question: question, answer: answer, plan: plan }, formats: [:html] %> +
+
-
+
+
+
+ <%= link_to(_('Add Comment'), "#note_new#{question.id}", class: "btn btn-default note_new_link", role: "button", style: "visibility: hidden") %> +
+
+
+ <% end %>
-<% end %> \ No newline at end of file +
\ No newline at end of file diff --git a/app/views/notes/_show.html.erb b/app/views/notes/_show.html.erb index 648406bc03..ea22cae13c 100644 --- a/app/views/notes/_show.html.erb +++ b/app/views/notes/_show.html.erb @@ -4,7 +4,7 @@
  • <%= raw note.text %>
  • - + <%= "#{user.name} at #{l(note.updated_at, format: :custom)}" %>
  • diff --git a/app/views/phases/_edit_plan_answers.html.erb b/app/views/phases/_edit_plan_answers.html.erb index ce5f6d9e9e..7f76823c82 100644 --- a/app/views/phases/_edit_plan_answers.html.erb +++ b/app/views/phases/_edit_plan_answers.html.erb @@ -49,47 +49,42 @@
-
-
-
-
<%= raw section.description %>
- - - - <% section.questions.each do |question| %> - <% # Load the answer or create a new one - answers = question.plan_answers(plan.id) if plan.present? - if answers.present? - answer = answers.first - else - answer = Answer.new({ plan: plan, question: question }) - end - %> -
-
- -
-
" class="answer-locking">
-
" class="answer-form"> - <%= render(partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: readonly }) %> -
-
"> - <%= render(partial: '/answers/status', locals: { answer: answer }) %> +
+
+
<%= raw section.description %>
+ + <% section.questions.each_with_index do |question, i| %> + <% # Load the answer or create a new one + answers = question.plan_answers(plan.id) if plan.present? + if answers.present? + answer = answers.first + else + answer = Answer.new({ plan: plan, question: question }) + end + %> +
+
+ +
+
" class="answer-locking">
+
" class="answer-form"> + <%= render(partial: '/answers/new_edit', locals: { question: question, answer: answer, readonly: readonly, locking: false }) %> +
+
" class="mt-10"> + <%= render(partial: '/answers/status', locals: { answer: answer }) %> +
+
+ + <%= render partial: '/phases/guidances_notes', locals: { plan: plan, template: phase.template, question: question, answer: answer, question_guidance: question_guidance, guidance_groups: guidance_groups } %> +
-
- - <%= render partial: '/phases/guidances_notes', locals: { plan: plan, template: phase.template, question: question, answer: answer, question_guidance: question_guidance, guidance_groups: guidance_groups } %> -
-
- <% end %> -
-
+ <%= raw('
') if i != section.questions.length - 1 %> + <% end %> +
+
+
<% end %>
diff --git a/app/views/phases/_guidances_notes.html.erb b/app/views/phases/_guidances_notes.html.erb index 3a8c905084..11d175e4fe 100644 --- a/app/views/phases/_guidances_notes.html.erb +++ b/app/views/phases/_guidances_notes.html.erb @@ -4,7 +4,7 @@ <% guidances_active = (annotations.present? || guidance_set.length > 0) %>
-
- <%= yield %> +
+
+ <%= yield %> +
+
diff --git a/app/views/questions/_new_edit_question_textarea.html.erb b/app/views/questions/_new_edit_question_textarea.html.erb index 755e6b37ba..13f0934b52 100644 --- a/app/views/questions/_new_edit_question_textarea.html.erb +++ b/app/views/questions/_new_edit_question_textarea.html.erb @@ -1,5 +1,17 @@ -<%# locals: { f, question, answer } %> +<%# locals: { f, question, answer, locking } %> +<%# + When locking variable is true, this partial renders a plain paragraph with the question answer or default value instead. + Since the partial answers/_locking reuses the partial answers/_new_edit which reuses this partial within, when a stale answer is found + the browser renders two forms (first the stale answer form followed by the latest answer saved). This reusability comes with a side-effect, + i.e. the browser might end up with duplicate ids for the form controls and therefore re-loading tinymce for the locking form becomes + rather cumbersome. As such, this workaround, simplifies the logic when a stale answer is found by rendering the html of the answer directly + within a paragraph. +%>
<%= f.label(:text, raw(question.text), class: 'control-label') %> - <%= text_area_tag('answer[text]', answer.text || question.default_value, id: "answer-text-#{question.id}", class: "form-control tinymce_answer") %> + <% if locking %> + <%= raw("

#{answer.text || question.default_value}

") %> + <% else %> + <%= text_area_tag('answer[text]', answer.text || question.default_value, id: "answer-text-#{question.id}", class: "form-control tinymce_answer") %> + <% end %>
\ No newline at end of file diff --git a/app/views/sections/_progress.html.erb b/app/views/sections/_progress.html.erb index 50167dd21f..55f705ed49 100644 --- a/app/views/sections/_progress.html.erb +++ b/app/views/sections/_progress.html.erb @@ -4,5 +4,4 @@ <% num_section_answers = section.num_answered_questions(plan.id) %> (<%= num_section_answers %> / <%= num_section_questions %>) - \ No newline at end of file diff --git a/app/views/shared/_access_controls.html.erb b/app/views/shared/_access_controls.html.erb index 624939d9bb..7ab3e50465 100644 --- a/app/views/shared/_access_controls.html.erb +++ b/app/views/shared/_access_controls.html.erb @@ -14,10 +14,18 @@
- <%= render :partial => 'shared/sign_in_form' %> +
+
+ <%= render :partial => 'shared/sign_in_form' %> +
+
-
- <%= render :partial => 'shared/create_account_form', locals: {extended: false} %> +
+
+
+ <%= render :partial => 'shared/create_account_form', locals: {extended: false} %> +
+
\ No newline at end of file diff --git a/lib/assets/javascripts/utils/expandCollapseAll.js b/lib/assets/javascripts/utils/expandCollapseAll.js index 4b4f842213..f77991584d 100644 --- a/lib/assets/javascripts/utils/expandCollapseAll.js +++ b/lib/assets/javascripts/utils/expandCollapseAll.js @@ -23,25 +23,33 @@ import 'bootstrap-sass/assets/javascripts/bootstrap.min'; *
* *
- *
- *
- *
- * This is test section A. + *
+ *
+ * This is test section A. + *
*
*
*
- *
*/ export default () => { - $.each($('.accordion-controls'), (idx, el) => { - const accordion = $(el).attr('data-parent'); - - $.each($(el).children('a[data-toggle-direction]'), (i, a) => { - $(a).click((event) => { - event.preventDefault(); - $(`#${accordion}`).children('div.panel-collapse').collapse(`${$(a).attr('data-toggle-direction')}`); + $('.accordion-controls').on('click', (e) => { + e.preventDefault(); + const currentTarget = $(e.currentTarget); + const target = $(e.target); + const direction = target.attr('data-toggle-direction'); + if (direction) { + // Selects all .panel elements where the parent is currentTarget.attr('data-parent') and + // after gets the immediately children whose class selector is panel-collapse + $(`#${currentTarget.attr('data-parent')} > .panel`).children('.panel-collapse').each((i, el) => { + const panelCollapse = $(el); + // Expands or collapses the panel according to the + // direction passed (e.g. show --> expands, hide --> collapses) + $(el).collapse(direction); + // Sets icon at panel-title accordingly + panelCollapse.prev().find('i.fa') + .removeClass('fa-plus fa-minus').addClass(direction === 'show' ? 'fa-minus' : 'fa-plus'); }); - }); + } }); }; diff --git a/lib/assets/javascripts/views/answers/edit.js b/lib/assets/javascripts/views/answers/edit.js index a36e8ff843..61da461a7a 100644 --- a/lib/assets/javascripts/views/answers/edit.js +++ b/lib/assets/javascripts/views/answers/edit.js @@ -8,9 +8,9 @@ import TimeagoFactory from '../../utils/timeagoFactory'; $(() => { const editorClass = 'tinymce_answer'; - const showSavingMessage = jQuery => jQuery.closest('.question-form').find('.saving-message').show(); - const hideSavingMessage = jQuery => jQuery.closest('.question-form').find('.saving-message').hide(); - const closestUnsavedMessage = jQuery => jQuery.closest('.question-form').find('.unsaved-message'); + const showSavingMessage = jQuery => jQuery.closest('.question-form').find('[data-status="saving"]').show(); + const hideSavingMessage = jQuery => jQuery.closest('.question-form').find('[data-status="saving"]').hide(); + const closestErrorSavingMessage = jQuery => jQuery.closest('.question-form').find('[data-status="error-saving"]'); const questionId = jQuery => jQuery.closest('.form-answer').attr('data-autosave'); const isStale = jQuery => jQuery.closest('.question-form').find('.answer-locking').html().length !== 0; const isReadOnly = () => $('.form-answer fieldset:disabled').length > 0; @@ -39,12 +39,16 @@ $(() => { TimeagoFactory.render($('time.timeago')); } if (isString(data.question.locking)) { // When an answer is stale... + // Removes event handlers for the saved form detachEventHandlers(form); // eslint-disable-line no-use-before-define - // Reflesh views for this context - $(`#answer-locking-${data.question.id}`).html(data.question.locking); + // Reflesh form view with the new partial form received $(`#answer-form-${data.question.id}`).html(data.question.form); + // Retrieves the newly form added to the DOM const newForm = $(`#answer-form-${data.question.id}`).find('form'); + // Attaches event handlers for the new form attachEventHandlers(newForm); // eslint-disable-line no-use-before-define + // Refresh optimistic locking view with the form that caused the locking + $(`#answer-locking-${data.question.id}`).html(data.question.locking); } else { // When answer is NOT stale... $(`#answer-locking-${data.question.id}`).html(''); if (isNumber(data.question.answer_lock_version)) { @@ -68,7 +72,7 @@ $(() => { } }; const failCallback = (error, jQuery) => { - closestUnsavedMessage(jQuery).html( + closestErrorSavingMessage(jQuery).html( (isObject(error.responseJSON) && isString(error.responseJSON.detail)) ? error.responseJSON.detail : error.statusText).show(); }; @@ -157,6 +161,7 @@ $(() => { Tinymce.init({ selector: `#${tinymceId}` }); editorHandlers(Tinymce.findEditorById(tinymceId)); }; + // Initial load TimeagoFactory.render($('time.timeago')); Tinymce.init({ selector: `.${editorClass}` }); if (!isReadOnly()) { diff --git a/lib/assets/stylesheets/application.scss b/lib/assets/stylesheets/application.scss index 54c93c83d4..778a270317 100644 --- a/lib/assets/stylesheets/application.scss +++ b/lib/assets/stylesheets/application.scss @@ -4,12 +4,6 @@ $icon-font-path: "~bootstrap-sass/assets/fonts/bootstrap/"; $fa-font-path: "~font-awesome/fonts/"; @import "~font-awesome/scss/font-awesome"; -/* CSSs files for jquery-ui. TODO, remove when bootstrap is re-introduced -*= require jquery-ui-dist/jquery-ui.min -*= require jquery-ui-dist/jquery-ui.structure.min -*= require jquery-ui-dist/jquery-ui.theme.min -*/ - @import "/overrides.scss"; [class^="bg-"] { @@ -18,3 +12,15 @@ $fa-font-path: "~font-awesome/fonts/"; .mb-5 { margin-bottom: 5px; } +.mb-10 { + margin-bottom: 10px; +} +.mt-10 { + margin-top: 10px; +} +.status { + display:block; + float: left; + clear: left; + margin-bottom: 10px; +} \ No newline at end of file diff --git a/lib/assets/stylesheets/overrides.scss b/lib/assets/stylesheets/overrides.scss index bd290b4fbe..a5b781cd64 100644 --- a/lib/assets/stylesheets/overrides.scss +++ b/lib/assets/stylesheets/overrides.scss @@ -84,44 +84,51 @@ thead th.table-scope { } } -/* TABS STYLING */ - +/* nav-tabs and nav-pills styling */ .nav-tabs, .nav-pills { background-color: $grey; color: $white; - border: none; - margin-right:2px; - margin-bottom: 10px; + border-bottom: 0px; } .nav-tabs > li > a, .nav-pills > li > a { - border-radius: 2px; color: $white; } -.nav-tabs > li > a:hover, .nav-pills > li > a:hover { +.nav-tabs > li > a:hover, +.nav-tabs > li > a:focus, +.nav-tabs > li.active > a, +.nav-tabs > li.active > a:focus, +.nav-tabs > li.active > a:hover { background-color: $white; color: $grey; - border:1px solid $grey; - border-bottom: 1px solid $white; /* white border prevents tabs from shifting on mouseover/mouseout */ -} -.nav-tabs > li.active > a, .nav-pills >li.active > a, -.nav-tabs > li.active > a:focus, .nav-pills > li.active > a:focus, -.nav-tabs > li.active > a:hover, .nav-pills > li.active > a:hover { - background-color: $white !important; - color:$grey; - border:1px solid $grey; - border-bottom: 1px solid $white; /* white border prevents tabs from shifting on mouseover/mouseout */ + border: 1px solid $grey; + border-bottom-color: transparent; /* Only nav-tabs have a transparent border-bottom */ } .nav-pills > li > a:hover, +.nav-pills > li > a:focus, .nav-pills > li.active > a, .nav-pills > li.active > a:focus, -.nav-pills > li.active > a:hover, { - border-bottom: 1px solid $grey; +.nav-pills > li.active > a:hover { + background-color: $white; + color: $grey; + border: 1px solid $grey; } +.nav-pills > li:last-child > a:hover, +.nav-pills > li:last-child > a:focus, +.nav-pills > li.active:last-child > a, +.nav-pills > li.active:last-child > a:focus, +.nav-pills > li.active:last-child > a:hover { + border-right: 0px; +} +/* panel styling */ -/* PANEL STYLING */ +.panel-default { + border-color: $grey; +} .panel-default > .panel-heading { background-color: $grey; color: $white; + border-top-right-radius: 0px; + border-top-left-radius: 0px; } .panel-title a { @@ -136,6 +143,21 @@ thead th.table-scope { color: $grey; } +/* list-group styling */ +.list-group-item:last-child { + border-bottom-right-radius: 0px; + border-bottom-left-radius: 0px; +} +.list-group-item:first-child { + border-top-right-radius: 0px; + border-top-left-radius: 0px; +} + +/* label stylying */ +.label { + font-size: 85%; +} + /* LIST STYLING */ span.sublist { margin-left: -15px; @@ -287,13 +309,8 @@ $header-text-color: $black; $header-background: linear-gradient(#ED6406, #F59503); */ -#app-navbar { - border-radius: 0px; -} - /* For org navbar custom height */ #org-navbar { - border-radius: 0px; margin-top: -20px; #banner-org-name { @@ -457,3 +474,18 @@ $tooltip-background: $grey; .create-plan-mock { margin-left: -40px; } + +/* Sharp edges */ +#app-navbar, +#org-navbar, +.nav-tabs > li > a, +.nav-pills > li > a, +.panel, .panel-group .panel, +.progress, +.btn, +.form-control, +.label, +.alert, +.input-group-addon { + border-radius: 0px; +} \ No newline at end of file