diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 2330bd8..ee20348 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -6,65 +6,83 @@ def create @note = Note.new - user_id = params[:new_note][:user_id] - @note.user_id = user_id - answer_id = params[:new_note][:answer_id] - question_id = params[:new_note][:question_id] - plan_id = params[:new_note][:plan_id] + @note.user_id = params[:note][:user_id] # create answer if we dont already have one - if answer_id.present? - answer = Answer.find(answer_id) + if params[:note][:answer_id].present? + @answer = Answer.find(params[:note][:answer_id]) else - answer = Answer.new - answer.plan_id = plan_id - answer.question_id = question_id - answer.user_id = user_id - answer.save! + @answer = Answer.new + @answer.plan_id = params[:note][:plan_id] + @answer.question_id = params[:note][:question_id] + @answer.user_id = @note.user_id + @answer.save! end - @note.answer= answer - @note.text = params["#{question_id}new_note_text"] + @note.answer = @answer + @note.text = params[:note][:text] authorize @note - @plan = answer.plan - @answer = answer - @question = Question.find(question_id) + @plan = @answer.plan + + @question = Question.find(params[:note][:question_id]) if @note.save @status = true @notice = success_message(_('comment'), _('created')) + render(json: { + "notes" => { + "id" => params[:note][:question_id], + "html" => render_to_string(partial: 'layout', locals: {plan: @plan, question: @question, answer: @answer }, formats: [:html]) + }, + "title" => { + "id" => params[:note][:question_id], + "html" => render_to_string(partial: 'title', locals: { answer: @answer}, formats: [:html]) + } + }.to_json, status: :created) else @status = false @notice = failed_create_error(@note, _('note')) + render json: { + "msg" => @notice + }.to_json, status: :bad_request end - notes = answer.notes.all - @num_notes = notes.count end - - def update - @note = Note.find(params[:note][:id]) + @note = Note.find(params[:id]) authorize @note - @note.text = params["#{params[:note][:id]}_note_text"] + @note.text = params[:note][:text] @answer = @note.answer @question = @answer.question @plan = @answer.plan + question_id = @note.answer.question_id.to_s + if @note.update_attributes(params[:note]) @notice = success_message(_('comment'), _('saved')) + render(json: { + "notes" => { + "id" => question_id, + "html" => render_to_string(partial: 'layout', locals: {plan: @plan, question: @question, answer: @answer }, formats: [:html]) + }, + "title" => { + "id" => question_id, + "html" => render_to_string(partial: 'title', locals: { answer: @answer}, formats: [:html]) + } + }.to_json, status: :ok) else @notice = failed_update_error(@note, _('note')) + render json: { + "msg" => @notice + }.to_json, status: :bad_request end end - - def archive - @note = Note.find(params[:note][:id]) + @note = Note.find(params[:id]) authorize @note @note.archived = true @note.archived_by = params[:note][:archived_by] @@ -73,10 +91,25 @@ @question = @answer.question @plan = @answer.plan + question_id = @note.answer.question_id.to_s + if @note.update_attributes(params[:note]) @notice = success_message(_('comment'), _('removed')) + render(json: { + "notes" => { + "id" => question_id, + "html" => render_to_string(partial: 'layout', locals: {plan: @plan, question: @question, answer: @answer }, formats: [:html]) + }, + "title" => { + "id" => question_id, + "html" => render_to_string(partial: 'title', locals: { answer: @answer}, formats: [:html]) + } + }.to_json, status: :ok) else @notice = failed_destroy_error(@note, _('note')) + render json: { + "msg" => @notice + }.to_json, status: :bad_request end end end diff --git a/app/models/answer.rb b/app/models/answer.rb index f44987b..4458ef6 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -70,4 +70,12 @@ return false end end + # Returns all the notes for an instance answer whose archived is nil or false. The Array is ordered by updated_at (descending) + def non_archived_notes + answer = Answer.includes(:notes).where({ id: self.id, notes: { archived: [nil, false] } }).order('notes.updated_at DESC').first + if !answer.nil? + return answer.notes.to_a + end + return [] + end end diff --git a/app/views/annotations/_show.html.erb b/app/views/annotations/_show.html.erb index e5f122b..792a720 100644 --- a/app/views/annotations/_show.html.erb +++ b/app/views/annotations/_show.html.erb @@ -2,16 +2,12 @@
- - <%= render partial: 'guidance_section', locals: {plan: @plan, question: question, answer: answer, question_guidances: @question_guidances} %> + + <%= render partial: 'guidances_notes', locals: {plan: @plan, question: question, answer: answer, question_guidance: @question_guidance} %>
<% end %> @@ -82,9 +82,5 @@ <% end %> - \ No newline at end of file diff --git a/app/views/phases/_guidance_section.html.erb b/app/views/phases/_guidance_section.html.erb deleted file mode 100644 index 837f1d4..0000000 --- a/app/views/phases/_guidance_section.html.erb +++ /dev/null @@ -1,81 +0,0 @@ -
- <% comments = answer.notes.all %> - <% active_tab = nil %> - <%= hidden_field_tag :question_id, question.id, class: "question_id" %> - - - - -
-
- - - - -
- - - <% num_annotations = 0 %> - <% i = 0 %> - <% if annotations.present? %> - <% annotations.each do |annotation| %> - <%= render partial: 'annotations/show', locals: {plan: plan, annotation: annotation, question: question} %> - <% num_annotations += 1%> - <% i += 1 %> - <% end %> - <% end %> - - - <% guidances = @question_guidance[question.id] %> - <% guidance_accordion_id = num_annotations %> - <% guidances.each_pair do |theme, groups| %> - <% groups.each do |group| %> - <%= render partial: 'guidance_groups/show', - locals: {group: group, theme: theme, i: i, question: question, - guidance_accordion_id: guidance_accordion_id} %> - - <% guidance_accordion_id += 1 %> - <% i += 1 %> - <% end %> - <% end %> -
- -
- -
- -
- <%= render partial: 'note', locals: {plan: plan, question: question, answer: answer } %> -
-
- -
-
\ No newline at end of file diff --git a/app/views/phases/_guidances_notes.html.erb b/app/views/phases/_guidances_notes.html.erb new file mode 100644 index 0000000..ab46619 --- /dev/null +++ b/app/views/phases/_guidances_notes.html.erb @@ -0,0 +1,60 @@ + +<% annotations = question.annotations.where(type: Annotation.types[:guidance]) %> +<% guidances = question_guidance[question.id] %> +<% guidances_active = annotations.present? || guidances.present? %> +
+ + +
+
+ + +
+ + <% num_annotations = 0 %> + <% i = 0 %> + <% if annotations.present? %> + <% annotations.each do |annotation| %> + <%= render partial: 'annotations/show', locals: {plan: plan, annotation: annotation, question: question} %> + <% num_annotations += 1%> + <% i += 1 %> + <% end %> + <% end %> + + <% guidance_accordion_id = num_annotations %> + <% guidances.each_pair do |theme, groups| %> + <% groups.each do |group| %> + <%= render partial: 'guidance_groups/show', + locals: {group: group, theme: theme, i: i, question: question, + guidance_accordion_id: guidance_accordion_id} %> + + <% guidance_accordion_id += 1 %> + <% i += 1 %> + <% end %> + <% end %> +
+
+
+ <%= render partial: '/notes/layout', locals: {plan: plan, question: question, answer: answer } %> +
+
+
\ No newline at end of file diff --git a/app/views/phases/_note.html.erb b/app/views/phases/_note.html.erb deleted file mode 100644 index 6cdee6f..0000000 --- a/app/views/phases/_note.html.erb +++ /dev/null @@ -1,24 +0,0 @@ - -<% if answer.present? && answer.notes.any? - notes = answer.notes.all.to_a.sort! {|x,y| y.updated_at <=> x.updated_at } %> -<%= hidden_field_tag :question_id, question.id, class: "question_id" %> - -
- <%= link_to _('Add a Comment'), "#question-form-#{question.id}", - class: "link-as-button add_comment_button right", - onclick: "dmproadmap.notes.add(#{question.id})", - role: "button" %> -
-
- - <%= render partial: "/notes/list", - locals: {question_id: question.id, notes: notes, plan: plan} %> -
- - -<% else %> - <%= render partial: "/notes/add", locals: {answer: answer, question: question, plan_id: plan.id } %> -<% end %> diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index 0ad2611..55662be 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -9,3 +9,4 @@ import './views/plans/new'; import './views/plans/share'; import './views/devise/registrations/edit'; +import './views/notes/index'; diff --git a/lib/assets/javascripts/views/notes/add.js b/lib/assets/javascripts/views/notes/add.js deleted file mode 100644 index 7609aa2..0000000 --- a/lib/assets/javascripts/views/notes/add.js +++ /dev/null @@ -1,12 +0,0 @@ -(function(ctx){ - ctx.add = ctx.add || (function(questionId){ - if($ && questionId) { - $(".alert-notice").hide(); - $('.view_comment_class').hide(); - $('.edit_comment_class').hide(); - $('.archive_comment_class').hide(); - $('#add_comment_button_top_div_'+ questionId).hide(); - $('#add_comment_block_div_'+ questionId).show(); - } - }); -})(define('dmproadmap.notes')); \ No newline at end of file diff --git a/lib/assets/javascripts/views/notes/archive.js b/lib/assets/javascripts/views/notes/archive.js deleted file mode 100644 index 6197b59..0000000 --- a/lib/assets/javascripts/views/notes/archive.js +++ /dev/null @@ -1,21 +0,0 @@ -(function(ctx){ - ctx.archive = ctx.archive || (function(noteId, questionId){ - if($ && noteId && questionId) { - $('.edit_comment_class').hide(); - $('.view_comment_class').hide(); - $('.archive_comment_class').hide(); - $('#view_comment_div_'+ noteId).hide(); - $('#lastet_comment_div_'+ questionId).hide(); - $('#edit_comment_div_'+ noteId).hide(); - $('#add_comment_block_div_'+ questionId).hide(); - $('#archive_comment_div_'+ noteId).show() - $('#add_comment_button_top_div_'+ questionId).show(); - } - }); - ctx.archive.cancel = ctx.archive.cancel || (function(event, noteId){ - event.preventDefault(); - if($ && noteId){ - $('#archive_comment_div_'+noteId).hide(); - } - }); -})(define('dmproadmap.notes')); \ No newline at end of file diff --git a/lib/assets/javascripts/views/notes/edit.js b/lib/assets/javascripts/views/notes/edit.js deleted file mode 100644 index 36272c6..0000000 --- a/lib/assets/javascripts/views/notes/edit.js +++ /dev/null @@ -1,15 +0,0 @@ -(function(ctx){ - ctx.edit = ctx.edit || (function(noteId, questionId){ - if($ && noteId && questionId) { - $('.edit_comment_class').hide(); - $('.view_comment_class').hide(); - $('.archive_comment_class').hide(); - $('#lastet_comment_div_'+ questionId).hide(); - $('#view_comment_div_'+ noteId).hide(); - $('#archive_comment_div_'+ noteId).hide(); - $('#add_comment_block_div_'+ questionId).hide(); - $('#edit_comment_div_'+ noteId).show(); - $('#add_comment_button_top_div_'+ questionId).show(); - } - }); -})(define('dmproadmap.notes')); \ No newline at end of file diff --git a/lib/assets/javascripts/views/notes/index.js b/lib/assets/javascripts/views/notes/index.js new file mode 100644 index 0000000..1259cfe --- /dev/null +++ b/lib/assets/javascripts/views/notes/index.js @@ -0,0 +1,86 @@ +import { Tinymce } from '../../utils/tinymce'; +import { isObject, isString } from '../../utils/isType'; + +$(() => { + let currentViewSelector = null; + const success = (data) => { + if (isObject(data) && + isObject(data.notes) && + isString(data.notes.id) && + isString(data.notes.html) && + isObject(data.title) && + isString(data.title.id) && + isString(data.title.html)) { + clean(); // eslint-disable-line no-use-before-define + $(`#notes-${data.notes.id}`).html(data.notes.html); + $(`#notes-title-${data.title.id}`).html(data.title.html); + initOrReload(); // eslint-disable-line no-use-before-define + } + }; + const error = () => { + // TODO adequate error handling for network error + }; + const getAction = jQueryForm => jQueryForm.attr('action'); + const getMethod = jQueryForm => jQueryForm.attr('method'); + const noteNewLinkHandler = (e) => { + $(e.target).css('visibility', 'hidden'); + if (currentViewSelector) { + $(currentViewSelector).hide(); + } + currentViewSelector = $(e.target).attr('href'); + $(currentViewSelector).show(); + }; + const noteOtherLinkHandler = (e) => { + $(e.target).closest('.notes').find('.note_new_link').css('visibility', 'visible'); + if (currentViewSelector) { + $(currentViewSelector).hide(); + } + currentViewSelector = $(e.target).attr('href'); + $(currentViewSelector).show(); + }; + const newEditNoteHandler = (e) => { + e.preventDefault(); + const jQueryForm = $(e.target).closest('form'); + const formElements = jQueryForm.serializeArray(); + const noteText = formElements.find(el => el.name === 'note[text]'); + noteText.value = Tinymce.findEditorById($(e.target).closest('form').find('[name="note[text]"]').attr('id')).getContent(); + $.ajax({ + method: getMethod(jQueryForm), + url: getAction(jQueryForm), + data: formElements, + }).done(success, error); + }; + const archiveNoteDestroyHandler = (e) => { + e.preventDefault(); + const jQueryForm = $(e.target).closest('form'); + const formElements = jQueryForm.serializeArray(); + $.ajax({ + method: getMethod(jQueryForm), + url: getAction(jQueryForm), + data: formElements, + }).done(success, error); + }; + const archiveNoteCancelHandler = () => { + if (currentViewSelector) { + $(currentViewSelector).hide(); + } + }; + const eventHandlers = ({ attachment = 'off' }) => { + $('.notes .note_new_link')[attachment]('click', noteNewLinkHandler); + $('.notes .note_show_link, .notes .note_edit_link, .notes .note_archive_link')[attachment]('click', noteOtherLinkHandler); + $('.new_note')[attachment]('submit', newEditNoteHandler); + $('.edit_note')[attachment]('submit', newEditNoteHandler); + $('.archive_note')[attachment]('submit', archiveNoteDestroyHandler); + $('.archive_note button[type="button"]')[attachment]('click', archiveNoteCancelHandler); + }; + const initOrReload = () => { + Tinymce.init({ selector: '.note' }); + eventHandlers({ attachment: 'on' }); + }; + const clean = () => { + currentViewSelector = null; + eventHandlers({ attachment: 'off' }); + Tinymce.destroyEditorsByClassName('note'); + }; + initOrReload(); +}); diff --git a/lib/assets/javascripts/views/notes/show.js b/lib/assets/javascripts/views/notes/show.js deleted file mode 100644 index ed718a2..0000000 --- a/lib/assets/javascripts/views/notes/show.js +++ /dev/null @@ -1,16 +0,0 @@ -(function(ctx){ - ctx.show = ctx.show || (function(noteId, questionId){ - if($ && noteId && questionId) { - $(".alert-notice").hide(); - $('.view_comment_class').hide(); - $('.edit_comment_class').hide(); - $('.archive_comment_class').hide(); - $('#lastet_comment_div_'+ questionId).hide(); - $('#edit_comment_div_'+ noteId).hide(); - $('#archive_comment_div_'+ noteId).hide(); - $('#add_comment_block_div_'+ questionId).hide(); - $('#view_comment_div_'+ noteId).show(); - $('#add_comment_button_top_div_'+ questionId).show(); - } - }); -})(define('dmproadmap.notes')); \ No newline at end of file diff --git a/test/functional/notes_controller_test.rb b/test/functional/notes_controller_test.rb index 65bc55a..32848f4 100644 --- a/test/functional/notes_controller_test.rb +++ b/test/functional/notes_controller_test.rb @@ -42,88 +42,65 @@ # POST /notes (notes_path) # ---------------------------------------------------------- test "create a new note" do - params = {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, question_id: @question.id, - "#{@question.id}new_note_text": 'Test Note'} + params = {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, question_id: @question.id, text: 'Test Note'} # Should redirect user to the root path if they are not logged in! - post notes_path, {new_note: params} + post notes_path, {note: params} assert_unauthorized_redirect_to_root_path sign_in @user - post notes_path, {new_note: params}, {'ACCEPT': 'text/javascript'} + post notes_path, {note: params}, {'ACCEPT': 'application/json'} assert_response :success assert assigns(:note) assert assigns(:plan) assert assigns(:answer) assert assigns(:question) assert assigns(:notice) - assert assigns(:num_notes) -# TODO: We don't appear to be displaying the success/failure notice anywhere in the js.erb #assert_select '.welcome-message h2', _('Comment was successfully created.') assert_equal 'Test Note', Note.last.text, 'Expected the note to have been created' # No Answer - post notes_path, {new_note: {user_id: @user.id, plan_id: @plan.id, question_id: @question.id, - "#{@question.id}new_note_text": 'Test Note no Answer'}}, {'ACCEPT': 'text/javascript'} - assert_response :success - assert assigns(:note) - assert assigns(:plan) - assert assigns(:answer) - assert assigns(:question) - assert assigns(:notice) - assert assigns(:num_notes) -# TODO: We don't appear to be displaying the success/failure notice anywhere in the js.erb - #assert_select '.welcome-message h2', _('Comment was successfully created.') -# TODO: expected the new note to have been added :/ + post notes_path, {note: {user_id: @user.id, plan_id: @plan.id, question_id: @question.id}}, {'ACCEPT': 'application/json'} + assert_response :bad_request + # TODO: expected the new note to have been added :/ #assert_equal 'Test Note no Answer', Note.last.text, 'Expected the note to have been created even if there was no answer' # Invalid object - post notes_path, {new_note: {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, - question_id: @question.id}}, {'ACCEPT': 'text/javascript'} - assert_response :success + post notes_path, {note: {user_id: @user.id, answer_id: @answer.id, plan_id: @plan.id, + question_id: @question.id}}, {'ACCEPT': 'application/json'} + assert_response :bad_request assert assigns(:note) assert assigns(:plan) assert assigns(:answer) assert assigns(:question) assert assigns(:notice) - assert assigns(:num_notes) -# TODO: We don't appear to be displaying the success/failure notice anywhere in the js.erb - #assert_select '.welcome-message h2', _('Unable to save your changes.') end # PUT /notes/:id (note_path) # ---------------------------------------------------------- test "update the note" do # Should redirect user to the root path if they are not logged in! - put note_path(@note), {note: {id: @note.id}, "#{@question.id}new_note_text": 'Test Note'}, {'ACCEPT': 'text/javascript'} + put note_path(@note), { note: { text: 'Test Note' }, id: @note.id }, {'ACCEPT': 'application/json'} assert_unauthorized_redirect_to_root_path sign_in @user # Valid save - put note_path(@note), {note: {id: @note.id}, "#{@question.id}new_note_text": 'Test Note'}, {'ACCEPT': 'text/javascript'} + put note_path(@note), { note: {text: 'Test Note' }, id: @note.id }, {'ACCEPT': 'application/json'} assert_response :success assert assigns(:note) assert assigns(:plan) assert assigns(:answer) assert assigns(:question) assert assigns(:notice) -# TODO: We don't appear to be displaying the success/failure notice anywhere in the js.erb - #assert_select '.welcome-message h2', _('Comment was successfully created.') @note.reload assert_equal 'Test Note', @note.text, "expected the note's text to be 'Test Note'" # Invalid save - put note_path(@note), {note: {id: @note.id}, "#{@question.id}new_note_text": nil}, {'ACCEPT': 'text/javascript'} - assert_response :success - assert assigns(:note) - assert assigns(:plan) - assert assigns(:answer) - assert assigns(:question) + put note_path(@note), { note: { text: nil }, id: @note.id }, {'ACCEPT': 'application/json'} + assert_response :bad_request assert assigns(:notice) -# TODO: We don't appear to be displaying the success/failure notice anywhere in the js.erb - #assert_select '.welcome-message h2', _('Unable to save your changes.') assert_equal 'Test Note', @note.text, "expected the note's text to Still be 'Test Note'" end @@ -131,12 +108,12 @@ # ---------------------------------------------------------- test "delete the note" do # Should redirect user to the root path if they are not logged in! - patch archive_note_path(@note), {note: {id: @note.id, archived_by: @user.id}}, {'ACCEPT': 'text/javascript'} + patch archive_note_path(@note), { note: { archived_by: @user.id }, id: @note.id }, {'ACCEPT': 'application/json'} assert_unauthorized_redirect_to_root_path sign_in @user - patch archive_note_path(@note), {note: {id: @note.id, archived_by: @user.id}}, {'ACCEPT': 'text/javascript'} + patch archive_note_path(@note), { note: { archived_by: @user.id }, id: @note.id }, {'ACCEPT': 'application/json'} assert_response :success assert assigns(:note) assert assigns(:plan)