From 7d23831c25fce5a65778e30697c6df7552a5aedd Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Mon, 15 Jul 2024 21:32:02 +0200 Subject: [PATCH 1/8] JS: Re-do form for uploading support letters --- .../support_letters.js.coffee | 187 ++++++++---------- .../questions/_supporters_question.html.slim | 14 +- .../qae_form/_supporter_fields.html.slim | 36 +--- .../qae_form/_supporters_question.html.slim | 16 +- .../support_letters/_attachment.html.slim | 3 +- forms/award_years/v2025/qavs/qavs_step3.rb | 2 +- forms/qae_form_builder/supporters_question.rb | 10 + 7 files changed, 110 insertions(+), 158 deletions(-) diff --git a/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee b/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee index 45e28c79f..de5f8ad8c 100644 --- a/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee +++ b/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee @@ -7,12 +7,12 @@ window.SupportLetters = new_item_init: (el) -> SupportLetters.clean_up_system_tags(el) SupportLetters.enable_item_fields_and_controls(el) - SupportLetters.fileupload_init(el.find(".js-support-letter-attachment")) + SupportLetters.fileupload_init(el.find('.js-support-letter-attachment')) el.find('input,textarea,select').first().focus() fileupload_init: (el) -> $el = $(el) - parent = $el.closest(".govuk-form-group") + parent = $el.closest('.govuk-form-group') upload_done = (e, data) -> SupportLetters.clean_up_system_tags(parent) @@ -20,20 +20,23 @@ window.SupportLetters = if data.result['original_filename'] filename = data.result['original_filename'] else - filename = "File uploaded" - file_title = $("

" + filename + "

") - hidden_input = $("") - - parent.find(".govuk-error-message").html("") - parent.find(".govuk-error-message").closest(".govuk-form-group").removeClass("govuk-form-group--error") - parent.append(file_title) - parent.append(hidden_input) + filename = 'File uploaded' + + parent.find('.govuk-error-message').html('') + parent.find('.govuk-error-message').closest('.govuk-form-group').removeClass('govuk-form-group--error') + + label = $('

' + filename + '

') + hiddenInput = $("") + + parent.append(label) + parent.append(hiddenInput) SupportLetters.autosave() + SupportLetters.submit(e) failed = (error_message) -> SupportLetters.clean_up_system_tags(parent) - parent.find(".govuk-error-message").html(error_message) - parent.closest(".govuk-form-group").addClass("govuk-form-group--error") + parent.find('.govuk-error-message').html(error_message) + parent.closest('.govuk-form-group').addClass('govuk-form-group--error') success_or_error = (e, data) -> errors = data.result.errors @@ -44,108 +47,33 @@ window.SupportLetters = upload_done(e, data) $el.fileupload( - url: $el.closest(".list-add").data('attachments-url') + ".json" + url: $el.closest('.list-add').data('attachments-url') + '.json' forceIframeTransport: true dataType: 'json' formData: [ - { name: "authenticity_token", value: $("meta[name='csrf-token']").attr("content") } + { name: 'authenticity_token', value: $('meta[name="csrf-token"]').attr('content') } ] always: success_or_error ) clean_up_system_tags: (parent) -> - parent.find("input[type='hidden']").remove() - parent.find(".support-letter-attachment-filename").remove() + parent.find('input[type="hidden"]').remove() + parent.find('.support-letter-attachment-filename').remove() enable_item_fields_and_controls: (parent) -> - parent.find(".js-save-collection").removeClass("govuk-!-display-none") - parent.find(".visible-read-only").hide() - fields = parent.find("input") - fields.removeClass("read-only") - parent.find(".govuk-error-message").html("") - form_name_prefix = parent.find(".js-system-tag").data("new-hidden-input-name") - letter_id_hidden_input = $("").prop('type', 'hidden'). - prop('name', form_name_prefix) - parent.append(letter_id_hidden_input) - - disable_item_fields_and_controls: (parent) -> - parent.find(".js-save-collection").addClass("govuk-!-display-none") - parent.find(".visible-read-only").show() - fields = parent.find("input") - fields.addClass("read-only") + parent.find('.govuk-error-message').html('') + prefixed = parent.find('.js-system-tag').data('new-hidden-input-name') + hiddenInput = $('').prop('type', 'hidden').prop('name', prefixed) + parent.append(hiddenInput) save_collection_init: () -> - $(document).on 'click', '.js-save-collection', (e) -> - e.preventDefault() - e.stopPropagation() - - button = $(this) - parent = $(this).closest("li") - - if !button.hasClass("govuk-!-display-none") - save_url = button.data 'save-collection-url' - - first_name = parent.find(".js-support-letter-first-name").val() - last_name = parent.find(".js-support-letter-last-name").val() - email = parent.find(".js-support-letter-email").val() - relationship_to_nominee = parent.find(".js-support-letter-relationship-to-nominee").val() - attachment_id = parent.find(".js-support-letter-attachment-id").val() - - data = { - "support_letter": { - "first_name": first_name, - "last_name": last_name, - "relationship_to_nominee": relationship_to_nominee - } - } - - if attachment_id - data["support_letter"]["attachment"] = attachment_id - - if email - data["support_letter"]["email"] = email - - if save_url - $.ajax - url: save_url - type: 'post' - data: data - dataType: 'json' - success: (response) -> - parent.find(".js-support-entry-id").prop('value', response) - parent.find(".govuk-error-message").html("") - parent.removeClass("govuk-form-group--error") - parent.addClass("read-only") - parent.addClass("js-support-letter-received") - parent.closest('li').find("input[type='text']").each -> - show_el = $(this).closest(".govuk-form-group").find(".visible-read-only") - show_el.text($(this).val()) - SupportLetters.disable_item_fields_and_controls(parent) - window.FormValidation.validateStep() - SupportLetters.autosave() - SupportLetters.showRemoveLink(parent, data, response) - - return - error: (response) -> - parent.find(".govuk-error-message").html("") - parent.removeClass("govuk-form-group--error") - error_message = response.responseText - $.each $.parseJSON(response.responseText), (question_key, error_message) -> - key_selector = ".js-support-letter-" + question_key.replace(/_/g, "-") - field_error_container = parent.find(key_selector). - closest(".govuk-form-group"). - find(".govuk-error-message") - field_error_container.html(error_message[0]) - field_error_container.closest(".govuk-form-group").addClass("govuk-form-group--error") - button.removeClass("govuk-visually-hidden") - - return + $(document).on 'change', '.js-trigger-autosave', debounce(SupportLetters.submit, 1000) autosave: () -> url = $('form.qae-form').data('autosave-url') if url # Setting current_step_id to form as we updating only current section form_data (not whole form) - $("#current_step_id").val($(".js-step-condition.step-current").attr("data-step")) + $('#current_step_id').val($('.js-step-condition.step-current').attr('data-step')) form_data = $('form.qae-form').serialize() $.ajax({ @@ -155,8 +83,63 @@ window.SupportLetters = dataType: 'json' }) - showRemoveLink: (parent, data, response) -> - removeLink = $(".remove-supporter", parent) - removeLink.data("url", "/users/form_answers/#{response['form_answer_id']}/support_letters/#{response['id']}") - removeLink.attr("aria-label", "Delete support letter from #{data['support_letter']['first_name']} #{data['support_letter']['last_name']}") - removeLink.removeClass("govuk-!-display-none") + submit: (e) -> + e.preventDefault() + e.stopPropagation() + + target = $(e.target) + parent = target.closest("li") + + data = {'support_letter': {}} + data['support_letter']['first_name'] = parent.find('.js-support-letter-first-name').val() + data['support_letter']['last_name'] = parent.find('.js-support-letter-last-name').val() + data['support_letter']['relationship_to_nominee'] = parent.find('.js-support-letter-relationship-to-nominee').val() + + attachmentId = parent.find('.js-support-letter-attachment-id').val() + if attachmentId + data['support_letter']['attachment'] = attachmentId + + email = parent.find('.js-support-letter-email').val() + if email + data['support_letter']['email'] = email + + saveUrl = parent.data('save-collection-url') + + if saveUrl + $.ajax + url: saveUrl + type: 'post' + data: data + dataType: 'json' + success: (response) -> + parent.find('.js-support-entry-id').prop('value', response) + parent.find('.govuk-error-message').html('') + parent.removeClass('govuk-form-group--error') + parent.addClass('js-support-letter-received') + window.FormValidation.validateStep() + SupportLetters.autosave() + + return + error: (response) -> + parent.find('.govuk-error-message').html('') + parent.removeClass('govuk-form-group--error') + msg = response.responseText + $.each $.parseJSON(response.responseText), (key, msg) -> + selector = '.js-support-letter-' + key.replace(/_/g, '-') + + errorContainer = parent.find(selector).closest('.govuk-form-group').find('.govuk-error-message') + errorContainer.html(msg[0]) + errorContainer.closest('.govuk-form-group').addClass('govuk-form-group--error') + + return + +debounce = (fn, wait = 500) -> + last = (new Date) - wait + -> + now = new Date + + # Return if we haven't waited long enough + return if wait > (now - last) + + fn.apply null, arguments + last = now \ No newline at end of file diff --git a/app/views/assessor/form_answers/questions/_supporters_question.html.slim b/app/views/assessor/form_answers/questions/_supporters_question.html.slim index d3d4f472e..fafb8f9a0 100644 --- a/app/views/assessor/form_answers/questions/_supporters_question.html.slim +++ b/app/views/assessor/form_answers/questions/_supporters_question.html.slim @@ -1,16 +1,10 @@ input name="form[#{question.key}][array]" value="true" type="hidden" disabled="disabled" ul.list-add.supporters-list data-need-to-clear-example=true data-add-limit=question.limit data-attachments-url=(users_form_answer_support_letter_attachments_path(@form_answer)) data-example-has-file-field=true = render 'qae_form/supporter_fields_placeholder', question: question - - if question.entities.present? - - if question.entities.count < question.default.to_i - - question.entities.each_with_index do |supporter, index| - = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index - - else - - question.entities.each_with_index do |supporter, index| - = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index - - else - - (0...question.default.to_i).each do |index| - = render 'qae_form/supporter_fields', question: question, supporter: {}, index: index + + - question.entities.each_with_index do |supporter, index| + = supporter + = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index a.govuk-button.govuk-button--secondary.button-add.js-button-add href="#" disabled="disabled" | Add another letter of support diff --git a/app/views/qae_form/_supporter_fields.html.slim b/app/views/qae_form/_supporter_fields.html.slim index 531f51bd6..ea3c52850 100644 --- a/app/views/qae_form/_supporter_fields.html.slim +++ b/app/views/qae_form/_supporter_fields.html.slim @@ -1,5 +1,5 @@ - persisted = supporter["support_letter_id"].present? || supporter["supporter_id"].present? -li.js-add-example class="#{'read-only js-support-letter-received' if persisted}" +li[class=class_names("js-add-example", "js-support-letter-received" => persisted) data-save-collection-url=users_form_answer_support_letters_url(@form_answer)] input.js-support-entry-id type="hidden" name="form[#{question.key}][#{index}][support_letter_id]" value=supporter["support_letter_id"] *possible_read_only_ops(question.step.opts[:id]) .js-system-tag data-new-hidden-input-name="form[#{question.key}][#{index}][support_letter_id]" @@ -7,49 +7,25 @@ li.js-add-example class="#{'read-only js-support-letter-received' if persisted}" label.govuk-label for="form[#{question.key}][#{index}][first_name]" ' First Name span.govuk-error-message - - input.js-support-letter-first-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][first_name]" id="form[#{question.key}][#{index}][first_name]" type="text" value=supporter["first_name"] *possible_read_only_ops(question.step.opts[:id]) - p.govuk-body.visible-read-only = supporter["first_name"] - + input.js-support-letter-field.js-support-letter-first-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][first_name]" id="form[#{question.key}][#{index}][first_name]" type="text" value=supporter["first_name"] *possible_read_only_ops(question.step.opts[:id]) .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][last_name]" ' Surname span.govuk-error-message - - input.js-support-letter-last-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][last_name]" id="form[#{question.key}][#{index}][last_name]" type="text" value=supporter["last_name"] *possible_read_only_ops(question.step.opts[:id]) - p.govuk-body.visible-read-only = supporter["last_name"] - + input.js-support-letter-field.js-support-letter-last-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][last_name]" id="form[#{question.key}][#{index}][last_name]" type="text" value=supporter["last_name"] *possible_read_only_ops(question.step.opts[:id]) .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][relationship_to_nominee]" ' Relationship to Nominee span.govuk-error-message + input.js-support-letter-field.js-support-letter-relationship-to-nominee.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][relationship_to_nominee]" id="form[#{question.key}][#{index}][relationship_to_nominee]" type="text" value=supporter["relationship_to_nominee"] *possible_read_only_ops(question.step.opts[:id]) - input.js-support-letter-relationship-to-nominee.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][relationship_to_nominee]" id="form[#{question.key}][#{index}][relationship_to_nominee]" type="text" value=supporter["relationship_to_nominee"] *possible_read_only_ops(question.step.opts[:id]) - p.govuk-body.visible-read-only = supporter["relationship_to_nominee"] .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][letter_of_support]" ' Letter of Support span.govuk-error-message - input class="js-trigger-autosave js-support-letter-attachment govuk-input medium" name="form[#{question.key}][#{index}][letter_of_support]" id="form[#{question.key}][#{index}][letter_of_support]" type='file' *possible_read_only_ops(question.step.opts[:id]) + input class="js-support-letter-field js-trigger-autosave js-support-letter-attachment govuk-input medium" name="form[#{question.key}][#{index}][letter_of_support]" id="form[#{question.key}][#{index}][letter_of_support]" type='file' *possible_read_only_ops(question.step.opts[:id]) - if supporter['letter_of_support'].present? - = render "support_letters/attachment", question: question, - index: index, - attachment_id: supporter['letter_of_support'] - - p - button.govuk-button.js-save-collection class=(persisted ? "visuallyhidden" : "") data-save-collection-url=users_form_answer_support_letters_url(@form_answer) - | Submit letter of support - - - if current_form_is_editable? && !current_lieutenant && !current_assessor - - if persisted - - if supporter["support_letter_id"].present? && @form_answer.support_letters.find_by_id(supporter["support_letter_id"]) - - url = users_form_answer_support_letter_path(form_answer_id: @form_answer.id, id: supporter["support_letter_id"]) - - else - - url = "#" - - if current_user || policy(:support_letter).can_remove? - = link_to "Remove", url, class: "govuk-button govuk-button--warning remove-supporter remove-link js-remove-link", data: { url: url }, 'aria-label' => "Delete support letter from #{supporter["first_name"]} #{supporter["last_name"]}" - - else - = link_to "Remove", "#", class: "govuk-button govuk-button--warning remove-supporter remove-link js-remove-link govuk-!-display-none", data: { url: "#" }, 'aria-label' => "#" + = render "support_letters/attachment", question: question, index: index, attachment_id: supporter['letter_of_support'] diff --git a/app/views/qae_form/_supporters_question.html.slim b/app/views/qae_form/_supporters_question.html.slim index f01f627d6..4939e9bca 100644 --- a/app/views/qae_form/_supporters_question.html.slim +++ b/app/views/qae_form/_supporters_question.html.slim @@ -1,17 +1,7 @@ input name="form[#{question.key}][array]" value="true" type="hidden" *possible_read_only_ops(question.step.opts[:id]) ul.list-add.supporters-list data-need-to-clear-example=true data-add-limit=question.limit data-attachments-url=(users_form_answer_support_letter_attachments_path(@form_answer)) data-example-has-file-field=true = render 'qae_form/supporter_fields_placeholder', question: question - - if question.entities.present? - - if question.entities.count < question.default.to_i - - question.entities.each_with_index do |supporter, index| - = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index - - else - - question.entities.each_with_index do |supporter, index| - = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index - - else - - (0...question.default.to_i).each do |index| - = render 'qae_form/supporter_fields', question: question, supporter: {}, index: index + + - question.entities.each_with_index do |supporter, index| + = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index -- if current_form_is_editable? - a.govuk-button.govuk-button--secondary.button-add.js-button-add href="#" *possible_read_only_ops(question.step.opts[:id]) - | Add another letter of support diff --git a/app/views/support_letters/_attachment.html.slim b/app/views/support_letters/_attachment.html.slim index 7011af6b8..611b93a17 100644 --- a/app/views/support_letters/_attachment.html.slim +++ b/app/views/support_letters/_attachment.html.slim @@ -1,6 +1,5 @@ - file = support_letter_attachments[attachment_id.to_i] p.govuk-body.support-letter-attachment-filename - = render "shared/attachment_with_virus_check_status", item: file, - mount_name: :attachment + = render "shared/attachment_with_virus_check_status", item: file, mount_name: :attachment input.js-support-letter-attachment-id type="hidden" name="form[#{question.key}][#{index}][letter_of_support]" value=attachment_id *possible_read_only_ops diff --git a/forms/award_years/v2025/qavs/qavs_step3.rb b/forms/award_years/v2025/qavs/qavs_step3.rb index e10e928fa..1f1e78a34 100644 --- a/forms/award_years/v2025/qavs/qavs_step3.rb +++ b/forms/award_years/v2025/qavs/qavs_step3.rb @@ -29,7 +29,7 @@ def qavs_step3 ) classes "question-support-uploads" limit 2 - default 1 + default 2 end end end diff --git a/forms/qae_form_builder/supporters_question.rb b/forms/qae_form_builder/supporters_question.rb index 67a55a3d8..5933bf5fa 100644 --- a/forms/qae_form_builder/supporters_question.rb +++ b/forms/qae_form_builder/supporters_question.rb @@ -39,5 +39,15 @@ class SupportersQuestion < Question end class SupportersQuestionDecorator < MultiQuestionDecorator + def entities + super + + collection = [].fill({}, 0, limit) + @entities.each_with_index do |record, idx| + collection[idx] = record + end + + @entities = collection + end end end From 555e442feef3721a5da00c2804ae00a1f25e6905 Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Tue, 16 Jul 2024 14:26:06 +0200 Subject: [PATCH 2/8] Make JS form work when re-submitting --- .../support_letters.js.coffee | 18 +++++++--- .../users/support_letters_controller.rb | 34 ++++++++++++++++--- .../questions/_supporters_question.html.slim | 2 -- app/views/form/support_letters/new.html.slim | 13 +++---- .../qae_form/_supporter_fields.html.slim | 4 ++- .../qae_form/_supporters_question.html.slim | 2 -- config/routes.rb | 4 +-- 7 files changed, 51 insertions(+), 26 deletions(-) diff --git a/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee b/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee index de5f8ad8c..a9665aece 100644 --- a/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee +++ b/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee @@ -103,19 +103,27 @@ window.SupportLetters = if email data['support_letter']['email'] = email - saveUrl = parent.data('save-collection-url') + createUrl = parent.data('create-url') + updateUrl = parent.data('update-url') + persistUrl = updateUrl || createUrl - if saveUrl + type = if !!updateUrl + 'put' + else + 'post' + + if persistUrl $.ajax - url: saveUrl - type: 'post' + url: persistUrl + type: type data: data dataType: 'json' success: (response) -> - parent.find('.js-support-entry-id').prop('value', response) + parent.find('.js-support-entry-id').prop('value', response['id']) parent.find('.govuk-error-message').html('') parent.removeClass('govuk-form-group--error') parent.addClass('js-support-letter-received') + parent.attr('data-update-url', response['update_url']) window.FormValidation.validateStep() SupportLetters.autosave() diff --git a/app/controllers/users/support_letters_controller.rb b/app/controllers/users/support_letters_controller.rb index 940bb16c1..2421e939f 100644 --- a/app/controllers/users/support_letters_controller.rb +++ b/app/controllers/users/support_letters_controller.rb @@ -12,7 +12,7 @@ class Users::SupportLettersController < Users::BaseController expose(:support_letter) do form_answer.support_letters.new( support_letter_params.merge({ - user_id: current_user.try(:id) || form_answer.user_id, + user_id: current_user&.id || form_answer.user_id, manual: true, support_letter_attachment: attachment }) @@ -30,11 +30,35 @@ def create attachment.support_letter = support_letter attachment.save! - render json: { id: support_letter.id, form_answer_id: form_answer.id }, - status: :created + render json: { + id: support_letter.id, + form_answer_id: form_answer.id, + update_url: users_form_answer_support_letter_path(form_answer, support_letter) + }, status: :created else - render json: support_letter.errors.messages.to_json, - status: :unprocessable_entity + render json: support_letter.errors.messages.to_json, status: :unprocessable_entity + end + end + + def update + @support_letter = form_answer.support_letters.find(params[:id]) + + if @support_letter.update(support_letter_params) + attachment.support_letter = @support_letter + attachment.save! + + SupportLetterAttachment + .where(support_letter: @support_letter) + .where.not(id: attachment.id) + .destroy_all + + render json: { + id: @support_letter.id, + form_answer_id: form_answer.id, + update_url: users_form_answer_support_letter_path(form_answer, @support_letter) + }, status: :ok + else + render json: @support_letter.errors.messages.to_json, status: :unprocessable_entity end end diff --git a/app/views/assessor/form_answers/questions/_supporters_question.html.slim b/app/views/assessor/form_answers/questions/_supporters_question.html.slim index fafb8f9a0..f8097888e 100644 --- a/app/views/assessor/form_answers/questions/_supporters_question.html.slim +++ b/app/views/assessor/form_answers/questions/_supporters_question.html.slim @@ -1,7 +1,5 @@ input name="form[#{question.key}][array]" value="true" type="hidden" disabled="disabled" ul.list-add.supporters-list data-need-to-clear-example=true data-add-limit=question.limit data-attachments-url=(users_form_answer_support_letter_attachments_path(@form_answer)) data-example-has-file-field=true - = render 'qae_form/supporter_fields_placeholder', question: question - - question.entities.each_with_index do |supporter, index| = supporter = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index diff --git a/app/views/form/support_letters/new.html.slim b/app/views/form/support_letters/new.html.slim index ae2bddb3a..dfa054332 100644 --- a/app/views/form/support_letters/new.html.slim +++ b/app/views/form/support_letters/new.html.slim @@ -27,18 +27,13 @@ header.page-header.group.page-header-over-sidebar = simple_form_for [:form, @form_answer, @support_letter], html: { class: 'qae-form' } do |f| .govuk-grid-row .govuk-grid-column-one-half - = f.input :first_name, - input_html: { class: "form-control" } - = f.input :last_name, - input_html: { class: "form-control" } - = f.input :relationship_to_nominee, - input_html: { class: "form-control" } + = f.input :first_name, input_html: { class: "form-control" } + = f.input :last_name, input_html: { class: "form-control" } + = f.input :relationship_to_nominee, input_html: { class: "form-control" } - f.object.support_letter_attachment || f.object.build_support_letter_attachment = f.simple_fields_for f.object.support_letter_attachment do |ff| - = ff.input :attachment, - as: :file, - input_html: { class: "form-control" } + = ff.input :attachment, as: :file, input_html: { class: "form-control" } = f.submit "Submit letter of support", class: "govuk-button" footer diff --git a/app/views/qae_form/_supporter_fields.html.slim b/app/views/qae_form/_supporter_fields.html.slim index ea3c52850..b7819c025 100644 --- a/app/views/qae_form/_supporter_fields.html.slim +++ b/app/views/qae_form/_supporter_fields.html.slim @@ -1,5 +1,7 @@ - persisted = supporter["support_letter_id"].present? || supporter["supporter_id"].present? -li[class=class_names("js-add-example", "js-support-letter-received" => persisted) data-save-collection-url=users_form_answer_support_letters_url(@form_answer)] +- create_url = users_form_answer_support_letters_url(@form_answer) +- update_url = users_form_answer_support_letter_path(@form_answer, supporter["support_letter_id"]) if persisted +li[class=class_names("js-add-example", "js-support-letter-received" => persisted) data-create-url=create_url data-update-url=update_url] input.js-support-entry-id type="hidden" name="form[#{question.key}][#{index}][support_letter_id]" value=supporter["support_letter_id"] *possible_read_only_ops(question.step.opts[:id]) .js-system-tag data-new-hidden-input-name="form[#{question.key}][#{index}][support_letter_id]" diff --git a/app/views/qae_form/_supporters_question.html.slim b/app/views/qae_form/_supporters_question.html.slim index 4939e9bca..81ae6406f 100644 --- a/app/views/qae_form/_supporters_question.html.slim +++ b/app/views/qae_form/_supporters_question.html.slim @@ -1,7 +1,5 @@ input name="form[#{question.key}][array]" value="true" type="hidden" *possible_read_only_ops(question.step.opts[:id]) ul.list-add.supporters-list data-need-to-clear-example=true data-add-limit=question.limit data-attachments-url=(users_form_answer_support_letter_attachments_path(@form_answer)) data-example-has-file-field=true - = render 'qae_form/supporter_fields_placeholder', question: question - - question.entities.each_with_index do |supporter, index| = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index diff --git a/config/routes.rb b/config/routes.rb index ec60f7ded..196d026db 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -93,7 +93,7 @@ resource :list_of_procedures, only: [:create, :destroy] resource :support_letter_attachments, only: :create resources :supporters, only: [:create, :destroy] - resources :support_letters, only: [:create, :show, :destroy] + resources :support_letters, only: [:create, :update, :show, :destroy] end resources :form_answer_feedbacks, only: [:show] end @@ -104,7 +104,7 @@ namespace :form do resources :form_answers do resources :supporters, only: [:new, :create, :index, :destroy] - resources :support_letters, only: [:new, :create, :destroy] + resources :support_letters, only: [:new, :update, :create, :destroy] resources :form_attachments, only: [:index, :new, :create, :destroy] end end From 84eb8ad01f4ce0c1078c176a239fda73e9eac72f Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Tue, 16 Jul 2024 23:06:17 +0200 Subject: [PATCH 3/8] Support letter upload via non-JS form --- .../form/support_letters_controller.rb | 120 ++++++------------ app/models/form_answer.rb | 7 +- app/models/support_letter.rb | 2 + app/models/support_letter_attachment.rb | 10 ++ .../form/support_letters/_form.html.slim | 24 ++++ .../support_letters/_support_letter.html.slim | 34 ----- app/views/form/support_letters/new.html.slim | 46 ------- app/views/form/supporters/index.html.slim | 10 +- config/routes.rb | 2 +- 9 files changed, 85 insertions(+), 170 deletions(-) create mode 100644 app/views/form/support_letters/_form.html.slim delete mode 100644 app/views/form/support_letters/_support_letter.html.slim delete mode 100644 app/views/form/support_letters/new.html.slim diff --git a/app/controllers/form/support_letters_controller.rb b/app/controllers/form/support_letters_controller.rb index cd0a41211..1c4066e0f 100644 --- a/app/controllers/form/support_letters_controller.rb +++ b/app/controllers/form/support_letters_controller.rb @@ -1,100 +1,58 @@ class Form::SupportLettersController < Form::BaseController - before_action :load_letter, only: :destroy - def create - @support_letter = @form_answer.support_letters.new( - support_letter_params.merge( - user_id: current_user.id, - manual: true - ) - ) - - attachment = SupportLetterAttachment.new(attachment_params) - attachment.user = current_user - attachment.form_answer = @form_answer - attachment.original_filename = attachment_params[:attachment].try(:original_filename) - @support_letter.support_letter_attachment = attachment + @form_answer.support_letters_attributes = permitted_params["support_letters_attributes"] - if @support_letter.save - add_support_letter_to_document! - @form_answer.save + unless @form_answer.valid? + @step = @form.steps.detect { |s| s.opts[:id] == :letters_of_support_step } - redirect_to form_form_answer_supporters_path(@form_answer) + render "form/supporters/index" else - render :new - end - end - - def new - @support_letter = @form_answer.support_letters.new - end + if @form_answer.save + add_support_letters_to_document! - def destroy - # safeguard for the case when not a user tries to delete a letter - return if current_assessor || current_lieutenant + redirect_to form_form_answer_supporters_path(@form_answer) + else + @step = @form.steps.detect { |s| s.opts[:id] == :letters_of_support_step } - if @support_letter.destroy - remove_support_letter_from_document! - @form_answer.save + render "form/supporters/index" + end end - - redirect_to form_form_answer_supporters_path(@form_answer) end private - def support_letter_params - params[:support_letter].permit( - :first_name, - :last_name, - :relationship_to_nominee + def permitted_params + params.require(:form_answer).permit( + support_letters_attributes: [ + :id, + :first_name, + :last_name, + :relationship_to_nominee, + :user_id, + :manual, + support_letter_attachment_attributes: [ + :id, + :attachment, + :attachment_cache, + :form_answer_id, + :user_id + ] + ] ) end - def attachment_params - if params[:support_letter] && params[:support_letter][:support_letter_attachment] - params[:support_letter][:support_letter_attachment].permit(:attachment) - else - {} - end - end - - def load_letter - @support_letter = @form_answer.support_letters.find(params[:id]) - end - - def add_support_letter_to_document! - letters = support_letters_doc - - new_letter = { - support_letter_id: @support_letter.id, - first_name: @support_letter.first_name, - last_name: @support_letter.last_name, - relationship_to_nominee: @support_letter.relationship_to_nominee, - letter_of_support: @support_letter.support_letter_attachment.id - } - - letters << new_letter - - @form_answer.document = @form_answer.document.merge(supporter_letters_list: letters, - manually_upload: "yes") - end - - def remove_support_letter_from_document! - letters = support_letters_doc - - letters.delete_if do |sup| - sup["support_letter_id"] == @support_letter.id + def add_support_letters_to_document! + list = @form_answer.support_letters.each_with_object([]) do |support_letter, memo| + memo << Hash[].tap do |h| + h[:support_letter_id] = support_letter.id + h[:first_name] = support_letter.first_name + h[:last_name] = support_letter.last_name + h[:relationship_to_nominee] = support_letter.relationship_to_nominee + h[:letter_of_support] = support_letter.support_letter_attachment.id + end end - @form_answer.document = @form_answer.document.merge(supporter_letters_list: letters) - end - - def support_letters_doc - if @form_answer.document["supporter_letters_list"].present? - @form_answer.document["supporter_letters_list"] - else - [] - end + @form_answer.document = @form_answer.document.merge(supporter_letters_list: list, manually_upload: "yes") + @form_answer.save! end end diff --git a/app/models/form_answer.rb b/app/models/form_answer.rb index 4f8a80ca5..c5b9d2adc 100644 --- a/app/models/form_answer.rb +++ b/app/models/form_answer.rb @@ -124,6 +124,8 @@ class FormAnswer < ApplicationRecord before_create :set_user_info end + accepts_nested_attributes_for :support_letters, reject_if: :dismiss_support_letters + store_accessor :document store_accessor :financial_data @@ -137,7 +139,6 @@ def state_machine end end - # FormAnswer#award_form # fetches relevant award form for the application's award year if available # else uses form for the current award year @@ -436,6 +437,10 @@ def award_form_class_name(year) "::AwardYears::V#{year}::QaeForms" end + def dismiss_support_letters(attributes) + %w(first_name last_name relationship_to_nominee).all? { |attr| attributes[attr].blank? } + end + def self.transition_class FormAnswerTransition end diff --git a/app/models/support_letter.rb b/app/models/support_letter.rb index a98559472..b018e75a1 100644 --- a/app/models/support_letter.rb +++ b/app/models/support_letter.rb @@ -22,6 +22,8 @@ class SupportLetter < ApplicationRecord scope :not_manual, -> { where(manual: false) } end + accepts_nested_attributes_for :support_letter_attachment + attr_accessor :attachment def full_name diff --git a/app/models/support_letter_attachment.rb b/app/models/support_letter_attachment.rb index 7ba459d4e..0ceb2e102 100644 --- a/app/models/support_letter_attachment.rb +++ b/app/models/support_letter_attachment.rb @@ -19,4 +19,14 @@ class SupportLetterAttachment < ApplicationRecord maximum: 5.megabytes.to_i } end + + begin :callbacks + before_save :assign_original_filename, if: :will_save_change_to_attachment? + end + + private + + def assign_original_filename + self.original_filename = self&.attachment&.identifier + end end diff --git a/app/views/form/support_letters/_form.html.slim b/app/views/form/support_letters/_form.html.slim new file mode 100644 index 000000000..3a79fb3c9 --- /dev/null +++ b/app/views/form/support_letters/_form.html.slim @@ -0,0 +1,24 @@ += simple_form_for [:form, @form_answer], url: [:form, @form_answer, :support_letters], method: :post, data: { turbo: false }, html: { class: 'qae-form' } do |f| + ul.list-add.supporters-list + = f.simple_fields_for :support_letters do |ff| + li + .govuk-grid-row + .govuk-grid-column-one-half + = ff.input :first_name, input_html: { class: "form-control" } + = ff.input :last_name, input_html: { class: "form-control" } + = ff.input :relationship_to_nominee, input_html: { class: "form-control" } + + = ff.input :manual, as: :hidden, input_html: { value: true } + = ff.input :user_id, as: :hidden, input_html: { value: current_user.id } + + = ff.simple_fields_for :support_letter_attachment, (ff.object.support_letter_attachment || ff.object.build_support_letter_attachment) do |fff| + = fff.input :attachment, as: :file, label: "Support letter", input_html: { class: "form-control" }, wrapper_html: { style: "margin-bottom: -1rem;" } + p.govuk-body.support-letter-attachment-filename + = render "shared/attachment_with_virus_check_status", item: fff.object, mount_name: :attachment + + = fff.input :attachment_cache, as: :hidden + + = fff.input :form_answer_id, as: :hidden, input_html: { value: @form_answer.id } + = fff.input :user_id, as: :hidden, input_html: { value: current_user.id } + + = f.submit "Submit letters of support", class: "govuk-button" \ No newline at end of file diff --git a/app/views/form/support_letters/_support_letter.html.slim b/app/views/form/support_letters/_support_letter.html.slim deleted file mode 100644 index 16fb89cef..000000000 --- a/app/views/form/support_letters/_support_letter.html.slim +++ /dev/null @@ -1,34 +0,0 @@ -li.view-only - .govuk-grid-row - .govuk-grid-column-one-half - label.govuk-label - ' First Name - p.govuk-body - = support_letter.first_name - - .govuk-grid-row - .govuk-grid-column-one-half - label.govuk-label - ' Last Name - p.govuk-body - = support_letter.last_name - - .govuk-grid-row - .govuk-grid-column-one-half - label.govuk-label - ' Relationship to Nominee - p.govuk-body - = support_letter.relationship_to_nominee - - .govuk-grid-row - .govuk-grid-column-full - label.govuk-label - ' Letter of Support - span.support-letter-attachment-filename.govuk-body - - if support_letter.support_letter_attachment - - file = support_letter.support_letter_attachment - = render "shared/attachment_with_virus_check_status", item: file, - mount_name: :attachment - span.clear - - = button_to "Remove", form_form_answer_support_letter_path(@form_answer, support_letter), method: :delete, class: "govuk-button govuk-button--warning #{'read_only' if admin_in_read_only_mode?}", 'aria-label' => "Delete support letter from #{support_letter.first_name} #{support_letter.last_name}" diff --git a/app/views/form/support_letters/new.html.slim b/app/views/form/support_letters/new.html.slim deleted file mode 100644 index dfa054332..000000000 --- a/app/views/form/support_letters/new.html.slim +++ /dev/null @@ -1,46 +0,0 @@ -- content_for :breadcrumbs do - li.govuk-breadcrumbs__list-item = link_to "Nominations", dashboard_path, class: 'govuk-breadcrumbs__link' - li.govuk-breadcrumbs__list-item = @form.title - -header.page-header.group.page-header-over-sidebar - div - h1.govuk-heading-xl - = @form.title - -.steps-progress-container - .steps-progress-content - = render 'qae_form/form_header', form: @form, current_step: "letters-of-support" - - .article-container.article-container-wider.step-article.step-current - article.group role="article" - div - fieldset.question-block - legend.govuk-label - span.steps.step-d-1 - span.visuallyhidden C 1 - span.todo C 1 - span class="govuk-body govuk-!-font-size-24 govuk-!-font-weight-bold govuk-!-display-block" - | New support letter - - ul.list-add - li - = simple_form_for [:form, @form_answer, @support_letter], html: { class: 'qae-form' } do |f| - .govuk-grid-row - .govuk-grid-column-one-half - = f.input :first_name, input_html: { class: "form-control" } - = f.input :last_name, input_html: { class: "form-control" } - = f.input :relationship_to_nominee, input_html: { class: "form-control" } - - - f.object.support_letter_attachment || f.object.build_support_letter_attachment - = f.simple_fields_for f.object.support_letter_attachment do |ff| - = ff.input :attachment, as: :file, input_html: { class: "form-control" } - = f.submit "Submit letter of support", class: "govuk-button" - - footer - nav.pagination.no-border aria-label="Pagination" role="navigation" - ul.group - li.previous.previous-alternate - = link_to edit_form_url(id: params[:form_answer_id], step: "letters-of-support"), rel: "prev", title: "Navigate to previous part", class: 'govuk-button govuk-button--secondary' do - span.pagination-label Back - - = render "qae_form/steps_progress_bar", current_step: "letters-of-support" diff --git a/app/views/form/supporters/index.html.slim b/app/views/form/supporters/index.html.slim index 9d3c5c64a..e89a93986 100644 --- a/app/views/form/supporters/index.html.slim +++ b/app/views/form/supporters/index.html.slim @@ -35,13 +35,9 @@ h1.govuk-heading-xl = help.title.html_safe .govuk-details__text == help.text - - ul.list-add.supporters-list - - @form_answer.support_letters.each do |support_letter| - = render "form/support_letters/support_letter", support_letter: support_letter - - = link_to "+ Add another support letter", new_form_form_answer_support_letter_path(@form_answer), class: "govuk-button govuk-button--secondary button-add" + + = render partial: "form/support_letters/form" = render "qae_form/steps_progress_bar", current_step: "letters-of-support" - = render "footer", step: @step + = render "form/supporters/footer", step: @step diff --git a/config/routes.rb b/config/routes.rb index 196d026db..e9bb73c10 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -104,7 +104,7 @@ namespace :form do resources :form_answers do resources :supporters, only: [:new, :create, :index, :destroy] - resources :support_letters, only: [:new, :update, :create, :destroy] + resources :support_letters, only: [:create] resources :form_attachments, only: [:index, :new, :create, :destroy] end end From 061b89c279a26395ca8dcb15d9102addad1c8ddc Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Wed, 17 Jul 2024 00:01:59 +0200 Subject: [PATCH 4/8] Make the non-JS form more seamless without additional buttons --- .../form/support_letters_controller.rb | 6 +++++- app/javascript/packs/application.scss | 1 + .../form/support_letters/_footer.html.slim | 21 +++++++++++++++++++ .../form/support_letters/_form.html.slim | 4 +++- app/views/form/supporters/index.html.slim | 2 -- 5 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 app/views/form/support_letters/_footer.html.slim diff --git a/app/controllers/form/support_letters_controller.rb b/app/controllers/form/support_letters_controller.rb index 1c4066e0f..19e0f7dbc 100644 --- a/app/controllers/form/support_letters_controller.rb +++ b/app/controllers/form/support_letters_controller.rb @@ -10,7 +10,11 @@ def create if @form_answer.save add_support_letters_to_document! - redirect_to form_form_answer_supporters_path(@form_answer) + if params[:next_step] + redirect_to edit_form_url(@form_answer, step: params[:next_step]) + else + redirect_to form_form_answer_supporters_path(@form_answer) + end else @step = @form.steps.detect { |s| s.opts[:id] == :letters_of_support_step } diff --git a/app/javascript/packs/application.scss b/app/javascript/packs/application.scss index e8dbc4ba2..9f3c03a8a 100644 --- a/app/javascript/packs/application.scss +++ b/app/javascript/packs/application.scss @@ -101,6 +101,7 @@ body.js-enabled { } .govuk-button { + color: inherit; margin-bottom: 0 !important; margin-top: 0 !important; white-space: nowrap; diff --git a/app/views/form/support_letters/_footer.html.slim b/app/views/form/support_letters/_footer.html.slim new file mode 100644 index 000000000..bfb5da726 --- /dev/null +++ b/app/views/form/support_letters/_footer.html.slim @@ -0,0 +1,21 @@ +nav.pagination.no-border class="#{'pagination-alternative' unless step.next}" role="navigation" aria-label="Pagination" + ul.group + - if step.previous + li.previous.previous-alternate.js-step-link data-step="step-#{step.previous.title_to_param}" + = link_to edit_form_url(id: @form_answer.id, step: step.previous.title_to_param), rel: "prev", title: "Navigate to previous part", class: 'govuk-button govuk-button--secondary' do + span.pagination-label + | Back + + - else + li.previous.previous-alternate + = link_to [:award_info, @form_answer.award_type.to_sym, form_id: @form_answer.id], rel: "prev", title: "Navigate to previous part", class: 'govuk-button govuk-button--secondary' do + span.pagination-label + | Back + + - unless admin_in_read_only_mode? + li.save-quit-link + = link_to "Save and come back later", dashboard_path, class: "save-quit-link govuk-button govuk-button--secondary" + + - if step.next + li.submit.js-next-link.js-step-link data-step="step-#{step.next.title_to_param}" + = f.submit "Save and continue", class: "govuk-button", title: "Submit letters of support" diff --git a/app/views/form/support_letters/_form.html.slim b/app/views/form/support_letters/_form.html.slim index 3a79fb3c9..327588cf3 100644 --- a/app/views/form/support_letters/_form.html.slim +++ b/app/views/form/support_letters/_form.html.slim @@ -1,4 +1,6 @@ = simple_form_for [:form, @form_answer], url: [:form, @form_answer, :support_letters], method: :post, data: { turbo: false }, html: { class: 'qae-form' } do |f| + = text_field_tag :next_step, @step.next.title_to_param, type: :hidden + ul.list-add.supporters-list = f.simple_fields_for :support_letters do |ff| li @@ -21,4 +23,4 @@ = fff.input :form_answer_id, as: :hidden, input_html: { value: @form_answer.id } = fff.input :user_id, as: :hidden, input_html: { value: current_user.id } - = f.submit "Submit letters of support", class: "govuk-button" \ No newline at end of file + = render partial: "form/support_letters/footer", locals: { f: f, step: @step } diff --git a/app/views/form/supporters/index.html.slim b/app/views/form/supporters/index.html.slim index e89a93986..0d632d0ae 100644 --- a/app/views/form/supporters/index.html.slim +++ b/app/views/form/supporters/index.html.slim @@ -39,5 +39,3 @@ h1.govuk-heading-xl = render partial: "form/support_letters/form" = render "qae_form/steps_progress_bar", current_step: "letters-of-support" - - = render "form/supporters/footer", step: @step From 8cbf17272ea882d53ab94ea780ef27bc77b1410f Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Wed, 17 Jul 2024 13:10:58 +0200 Subject: [PATCH 5/8] Update copy for non-JS version --- .../form/support_letters/_form.html.slim | 12 +++++--- forms/award_years/v2025/qavs/qavs_step3.rb | 29 ++++++++----------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/app/views/form/support_letters/_form.html.slim b/app/views/form/support_letters/_form.html.slim index 327588cf3..d16169c16 100644 --- a/app/views/form/support_letters/_form.html.slim +++ b/app/views/form/support_letters/_form.html.slim @@ -3,18 +3,22 @@ ul.list-add.supporters-list = f.simple_fields_for :support_letters do |ff| + - idx = ff.options[:child_index] + 1 li .govuk-grid-row .govuk-grid-column-one-half - = ff.input :first_name, input_html: { class: "form-control" } - = ff.input :last_name, input_html: { class: "form-control" } - = ff.input :relationship_to_nominee, input_html: { class: "form-control" } + label[class="govuk-label"] + span[class="govuk-body govuk-!-font-size-20 govuk-!-font-weight-bold govuk-!-display-block"] + = "Letter of Support #{idx}" + = ff.input :first_name, label: "First Name:", input_html: { class: "form-control" } + = ff.input :last_name, label: "Surname:", input_html: { class: "form-control" } + = ff.input :relationship_to_nominee, label: "Relationship to Group:", input_html: { class: "form-control" } = ff.input :manual, as: :hidden, input_html: { value: true } = ff.input :user_id, as: :hidden, input_html: { value: current_user.id } = ff.simple_fields_for :support_letter_attachment, (ff.object.support_letter_attachment || ff.object.build_support_letter_attachment) do |fff| - = fff.input :attachment, as: :file, label: "Support letter", input_html: { class: "form-control" }, wrapper_html: { style: "margin-bottom: -1rem;" } + = fff.input :attachment, as: :file, label: "Upload Letter of Support #{idx}", input_html: { class: "form-control" }, wrapper_html: { style: "margin-bottom: -1rem;" } p.govuk-body.support-letter-attachment-filename = render "shared/attachment_with_virus_check_status", item: fff.object, mount_name: :attachment diff --git a/forms/award_years/v2025/qavs/qavs_step3.rb b/forms/award_years/v2025/qavs/qavs_step3.rb index 1f1e78a34..17dd3dfb3 100644 --- a/forms/award_years/v2025/qavs/qavs_step3.rb +++ b/forms/award_years/v2025/qavs/qavs_step3.rb @@ -9,23 +9,18 @@ def qavs_step3 supporters :supporter_letters_list, "" do ref "C 1" context %( -

Before you start, please refer to our full guidance for letters of support as outlined on our website.

-

Please obtain two different letters that endorse the nominated group's contribution from people who are familiar with its work.

-

- Supporters must not be volunteers or paid workers in the group. Each letter should be no more than 500 words. The letters should be about the whole group, rather than just one volunteer and should help to show how its work is outstanding. -

-

Please list below the names of the supporters and their relationship (if any) to the group.

- ) - hint "What are the allowed file formats?", %( -

- Letter of support must be no larger than 5 MB. -

-

- They can be: -
- Images in jpg, jpeg and png formats; -
- Files in doc, docx, odt, pdf and txt formats. +

Letters of support are an essential part of your nomination, as they help to clarify and explain the impact of the nominated group’s work in the local community. You will need to provide 2 letters of support alongside your nomination.

+

For more information on what letters can cover, please see the Letters of Support page on our website.

+

Key criteria:

+
    +
  1. letters must be written by individuals who are familiar with the group’s work, for example: a beneficiary, local resident or member of a partner charity
  2. +
  3. letters must not be written by a volunteer, employee, trustee, or anyone involved in the running of the group
  4. +
  5. letters written by the nominator will be ineligible
  6. +
  7. each letter should be no more than 500 words
  8. +
  9. only 2 letters of support can be submitted
  10. +
+

For each letter uploaded below, please note the letter writer’s relationship to the group, for example: a beneficiary of the group, local resident or member of a partner charity.

+

Once uploaded, all files are saved automatically. If you make a mistake and upload the wrong letter, please use the same button to upload the correct file as it will automatically replace the previous version.

) classes "question-support-uploads" limit 2 From 52a41bd5dd4fbff78f8d0c2503d06a88a170134a Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Wed, 17 Jul 2024 15:34:02 +0200 Subject: [PATCH 6/8] Add support letter checkboxes into the form --- .../form/support_letters_controller.rb | 27 +++++------ app/controllers/form_controller.rb | 7 +-- app/javascript/packs/application.scss | 5 ++- .../questions/_supporters_question.html.slim | 4 -- .../form/support_letters/_form.html.slim | 45 +++++++++---------- app/views/form/supporters/index.html.slim | 14 +++++- .../qae_form/_supporter_fields.html.slim | 12 +++-- .../_supporter_fields_placeholder.html.slim | 12 +++-- forms/award_years/v2025/qavs/qavs_step3.rb | 18 ++++++++ 9 files changed, 85 insertions(+), 59 deletions(-) diff --git a/app/controllers/form/support_letters_controller.rb b/app/controllers/form/support_letters_controller.rb index 19e0f7dbc..fa581277a 100644 --- a/app/controllers/form/support_letters_controller.rb +++ b/app/controllers/form/support_letters_controller.rb @@ -1,25 +1,22 @@ class Form::SupportLettersController < Form::BaseController + include FormAnswerSubmissionMixin + def create @form_answer.support_letters_attributes = permitted_params["support_letters_attributes"] + @form_answer.document = prepare_doc if params[:form].present? + + if @form_answer.valid? && @form_answer.save + add_support_letters_to_document! - unless @form_answer.valid? + if params[:next_step_id] + redirect_to edit_form_url(@form_answer, step: params[:next_step_id]) + else + redirect_to form_form_answer_supporters_path(@form_answer) + end + else @step = @form.steps.detect { |s| s.opts[:id] == :letters_of_support_step } render "form/supporters/index" - else - if @form_answer.save - add_support_letters_to_document! - - if params[:next_step] - redirect_to edit_form_url(@form_answer, step: params[:next_step]) - else - redirect_to form_form_answer_supporters_path(@form_answer) - end - else - @step = @form.steps.detect { |s| s.opts[:id] == :letters_of_support_step } - - render "form/supporters/index" - end end end diff --git a/app/controllers/form_controller.rb b/app/controllers/form_controller.rb index 2316934c9..d9f16781f 100644 --- a/app/controllers/form_controller.rb +++ b/app/controllers/form_controller.rb @@ -115,11 +115,8 @@ def save redirect_to edit_form_url(@form_answer, step: params[:next_step]) else params[:step] = @form_answer.steps_with_errors.try(:first) - # avoid redirecting to supporters page - if !params[:step] || params[:step] == "letters-of-support" - params[:step] = @form.steps.first.title_to_param - end - + params[:step] ||= @form.steps.first.title_to_param + render template: "qae_form/show" end end diff --git a/app/javascript/packs/application.scss b/app/javascript/packs/application.scss index 9f3c03a8a..aef597acd 100644 --- a/app/javascript/packs/application.scss +++ b/app/javascript/packs/application.scss @@ -101,11 +101,14 @@ body.js-enabled { } .govuk-button { - color: inherit; margin-bottom: 0 !important; margin-top: 0 !important; white-space: nowrap; } + + a.govuk-button { + color: inherit; + } } .previous { diff --git a/app/views/assessor/form_answers/questions/_supporters_question.html.slim b/app/views/assessor/form_answers/questions/_supporters_question.html.slim index f8097888e..916785e16 100644 --- a/app/views/assessor/form_answers/questions/_supporters_question.html.slim +++ b/app/views/assessor/form_answers/questions/_supporters_question.html.slim @@ -1,8 +1,4 @@ input name="form[#{question.key}][array]" value="true" type="hidden" disabled="disabled" ul.list-add.supporters-list data-need-to-clear-example=true data-add-limit=question.limit data-attachments-url=(users_form_answer_support_letter_attachments_path(@form_answer)) data-example-has-file-field=true - question.entities.each_with_index do |supporter, index| - = supporter = render 'qae_form/supporter_fields', question: question, supporter: supporter, index: index - -a.govuk-button.govuk-button--secondary.button-add.js-button-add href="#" disabled="disabled" - | Add another letter of support diff --git a/app/views/form/support_letters/_form.html.slim b/app/views/form/support_letters/_form.html.slim index d16169c16..6e0f6eae1 100644 --- a/app/views/form/support_letters/_form.html.slim +++ b/app/views/form/support_letters/_form.html.slim @@ -1,30 +1,25 @@ -= simple_form_for [:form, @form_answer], url: [:form, @form_answer, :support_letters], method: :post, data: { turbo: false }, html: { class: 'qae-form' } do |f| - = text_field_tag :next_step, @step.next.title_to_param, type: :hidden - - ul.list-add.supporters-list - = f.simple_fields_for :support_letters do |ff| - - idx = ff.options[:child_index] + 1 - li - .govuk-grid-row - .govuk-grid-column-one-half - label[class="govuk-label"] - span[class="govuk-body govuk-!-font-size-20 govuk-!-font-weight-bold govuk-!-display-block"] - = "Letter of Support #{idx}" - = ff.input :first_name, label: "First Name:", input_html: { class: "form-control" } - = ff.input :last_name, label: "Surname:", input_html: { class: "form-control" } - = ff.input :relationship_to_nominee, label: "Relationship to Group:", input_html: { class: "form-control" } += f.simple_fields_for :support_letters do |ff| + - idx = ff.options[:child_index] + 1 + li + .govuk-grid-row + .govuk-grid-column-one-half + label[class="govuk-label"] + span[class="govuk-body govuk-!-font-size-20 govuk-!-font-weight-bold govuk-!-display-block"] + = "Letter of Support #{idx}" + = ff.input :first_name, label: "First Name:", input_html: { class: "form-control" } + = ff.input :last_name, label: "Surname:", input_html: { class: "form-control" } + = ff.input :relationship_to_nominee, label: "Relationship to Group:", input_html: { class: "form-control" } - = ff.input :manual, as: :hidden, input_html: { value: true } - = ff.input :user_id, as: :hidden, input_html: { value: current_user.id } + = ff.input :manual, as: :hidden, input_html: { value: true } + = ff.input :user_id, as: :hidden, input_html: { value: current_user.id } - = ff.simple_fields_for :support_letter_attachment, (ff.object.support_letter_attachment || ff.object.build_support_letter_attachment) do |fff| - = fff.input :attachment, as: :file, label: "Upload Letter of Support #{idx}", input_html: { class: "form-control" }, wrapper_html: { style: "margin-bottom: -1rem;" } - p.govuk-body.support-letter-attachment-filename - = render "shared/attachment_with_virus_check_status", item: fff.object, mount_name: :attachment + = ff.simple_fields_for :support_letter_attachment, (ff.object.support_letter_attachment || ff.object.build_support_letter_attachment) do |fff| + = fff.input :attachment, as: :file, label: "Upload Letter of Support #{idx}", input_html: { class: "form-control" }, wrapper_html: { style: "margin-bottom: -1rem;" } + p.govuk-body.support-letter-attachment-filename + = render "shared/attachment_with_virus_check_status", item: fff.object, mount_name: :attachment - = fff.input :attachment_cache, as: :hidden + = fff.input :attachment_cache, as: :hidden - = fff.input :form_answer_id, as: :hidden, input_html: { value: @form_answer.id } - = fff.input :user_id, as: :hidden, input_html: { value: current_user.id } + = fff.input :form_answer_id, as: :hidden, input_html: { value: @form_answer.id } + = fff.input :user_id, as: :hidden, input_html: { value: current_user.id } - = render partial: "form/support_letters/footer", locals: { f: f, step: @step } diff --git a/app/views/form/supporters/index.html.slim b/app/views/form/supporters/index.html.slim index 0d632d0ae..ad955ba7c 100644 --- a/app/views/form/supporters/index.html.slim +++ b/app/views/form/supporters/index.html.slim @@ -15,6 +15,7 @@ h1.govuk-heading-xl article.group role="article" div - letters_intro_question = @step.questions.detect { |q| q.key == :supporter_letters_list } + - checkbox_questions = @step.questions.select { |q| q.key.in?(%i(independent_individual not_nominator)) } .question-block label.govuk-label @@ -36,6 +37,17 @@ h1.govuk-heading-xl .govuk-details__text == help.text - = render partial: "form/support_letters/form" + = simple_form_for [:form, @form_answer], url: [:form, @form_answer, :support_letters], method: :post, data: { turbo: false }, html: { class: 'qae-form' } do |f| + = hidden_field_tag :current_step_id, @step.title_to_param + = hidden_field_tag :next_step_id, @step.next.title_to_param + + ul.list-add.supporters-list + = render partial: "form/support_letters/form", locals: { f: f } + + - checkbox_questions.each do |question| + .govuk-form-group + = render partial: "qae_form/confirm_question", locals: { question: question } + + = render partial: "form/support_letters/footer", locals: { f: f, step: @step } = render "qae_form/steps_progress_bar", current_step: "letters-of-support" diff --git a/app/views/qae_form/_supporter_fields.html.slim b/app/views/qae_form/_supporter_fields.html.slim index b7819c025..aee0fad3b 100644 --- a/app/views/qae_form/_supporter_fields.html.slim +++ b/app/views/qae_form/_supporter_fields.html.slim @@ -2,30 +2,34 @@ - create_url = users_form_answer_support_letters_url(@form_answer) - update_url = users_form_answer_support_letter_path(@form_answer, supporter["support_letter_id"]) if persisted li[class=class_names("js-add-example", "js-support-letter-received" => persisted) data-create-url=create_url data-update-url=update_url] + label[class="govuk-label"] + span[class="govuk-body govuk-!-font-size-20 govuk-!-font-weight-bold govuk-!-display-block"] + = "Letter of Support #{index + 1}" + input.js-support-entry-id type="hidden" name="form[#{question.key}][#{index}][support_letter_id]" value=supporter["support_letter_id"] *possible_read_only_ops(question.step.opts[:id]) .js-system-tag data-new-hidden-input-name="form[#{question.key}][#{index}][support_letter_id]" .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][first_name]" - ' First Name + ' First Name: span.govuk-error-message input.js-support-letter-field.js-support-letter-first-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][first_name]" id="form[#{question.key}][#{index}][first_name]" type="text" value=supporter["first_name"] *possible_read_only_ops(question.step.opts[:id]) .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][last_name]" - ' Surname + ' Surname: span.govuk-error-message input.js-support-letter-field.js-support-letter-last-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][last_name]" id="form[#{question.key}][#{index}][last_name]" type="text" value=supporter["last_name"] *possible_read_only_ops(question.step.opts[:id]) .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][relationship_to_nominee]" - ' Relationship to Nominee + ' Relationship to Group: span.govuk-error-message input.js-support-letter-field.js-support-letter-relationship-to-nominee.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][#{index}][relationship_to_nominee]" id="form[#{question.key}][#{index}][relationship_to_nominee]" type="text" value=supporter["relationship_to_nominee"] *possible_read_only_ops(question.step.opts[:id]) .govuk-form-group label.govuk-label for="form[#{question.key}][#{index}][letter_of_support]" - ' Letter of Support + = "Upload Letter of Support #{index + 1}" span.govuk-error-message input class="js-support-letter-field js-trigger-autosave js-support-letter-attachment govuk-input medium" name="form[#{question.key}][#{index}][letter_of_support]" id="form[#{question.key}][#{index}][letter_of_support]" type='file' *possible_read_only_ops(question.step.opts[:id]) diff --git a/app/views/qae_form/_supporter_fields_placeholder.html.slim b/app/views/qae_form/_supporter_fields_placeholder.html.slim index b95578a8b..1eb001785 100644 --- a/app/views/qae_form/_supporter_fields_placeholder.html.slim +++ b/app/views/qae_form/_supporter_fields_placeholder.html.slim @@ -1,30 +1,34 @@ li.js-add-example class="govuk-!-display-none" + label[class="govuk-label"] + span[class="govuk-body govuk-!-font-size-20 govuk-!-font-weight-bold govuk-!-display-block"] + = "Letter of Support #{index + 1}" + input.js-support-entry-id type="hidden" name="form[#{question.key}][{index}][support_letter_id]" disabled=true .js-system-tag data-new-hidden-input-name="form[#{question.key}][{index}][support_letter_id]" .govuk-form-group label.govuk-label for="form[#{question.key}][{index}][first_name]" - ' First Name + ' First Name: span.govuk-error-message input.js-support-letter-first-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][{index}][first_name]" id="form[#{question.key}][{index}][first_name]" type="text" disabled=true p.govuk-body.visible-read-only .govuk-form-group label.govuk-label for="form[#{question.key}][{index}][last_name]" - ' Surname + ' Surname: span.govuk-error-message input.js-support-letter-last-name.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][{index}][last_name]" id="form[#{question.key}][{index}][last_name]" type="text" disabled=true p.govuk-body.visible-read-only .govuk-form-group label.govuk-label for="form[#{question.key}][{index}][relationship_to_nominee]" - ' Relationship to Nominee + ' Relationship to Group: span.govuk-error-message input.js-support-letter-relationship-to-nominee.js-trigger-autosave.govuk-input autocomplete="off" class="js-trigger-autosave medium" name="form[#{question.key}][{index}][relationship_to_nominee]" id="form[#{question.key}][{index}][relationship_to_nominee]" type="text" disabled=true p.govuk-body.visible-read-only .govuk-form-group label.govuk-label for="form[#{question.key}][{index}][letter_of_support]" - ' Letter of Support + = "Upload Letter of Support #{index + 1}" span.govuk-error-message input class="js-trigger-autosave js-support-letter-attachment govuk-input medium" name="form[#{question.key}][{index}][letter_of_support]" id="form[#{question.key}][{index}][letter_of_support]" type='file' disabled=true diff --git a/forms/award_years/v2025/qavs/qavs_step3.rb b/forms/award_years/v2025/qavs/qavs_step3.rb index 17dd3dfb3..f0308e6be 100644 --- a/forms/award_years/v2025/qavs/qavs_step3.rb +++ b/forms/award_years/v2025/qavs/qavs_step3.rb @@ -26,6 +26,24 @@ def qavs_step3 limit 2 default 2 end + + confirm :independent_individual, "" do + required + text -> do + %( + I can confirm that these letters were written by individuals independent of the group. + ) + end + end + + confirm :not_nominator, "" do + required + text -> do + %( + I can confirm that these letters were not written by the nominator. + ) + end + end end end end From a4ffb9cd04003f55da7c295d0eb9b91ac5fe9250 Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Wed, 17 Jul 2024 15:55:03 +0200 Subject: [PATCH 7/8] Update `FormAnswer` factory --- ...answer_document_with_review_audit_certificate_criteria.json | 2 -- spec/fixtures/form_answer_qavs.json | 3 +++ spec/fixtures/form_answer_qavs_with_la.json | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/fixtures/form_answer_document_with_review_audit_certificate_criteria.json b/spec/fixtures/form_answer_document_with_review_audit_certificate_criteria.json index f8b7475ed..800f9e9b2 100644 --- a/spec/fixtures/form_answer_document_with_review_audit_certificate_criteria.json +++ b/spec/fixtures/form_answer_document_with_review_audit_certificate_criteria.json @@ -30,7 +30,6 @@ "employees_4of5":"", "employees_5of5":"", "head_job_title":"asdasd", - "parent_company":"asdasd", "sic_code":"0111", "net_profit_1of2":"122", "net_profit_1of5":"", @@ -133,7 +132,6 @@ "development_performance":"sad", "financial_year_date_day":"1", "leadership_contribution":"asdasd", - "parent_ultimate_control":"yes", "head_of_business_honours":"sadasd", "invoicing_unit_relations":"", "organization_address_region":"East", diff --git a/spec/fixtures/form_answer_qavs.json b/spec/fixtures/form_answer_qavs.json index 11686e905..d87fcc9d9 100644 --- a/spec/fixtures/form_answer_qavs.json +++ b/spec/fixtures/form_answer_qavs.json @@ -113,6 +113,9 @@ } ], + "independent_individual": "yes", + "not_nominator": "yes", + "nominator_title": "Mr", "nominator_name": "Nominator", "nominator_address_building": "94", diff --git a/spec/fixtures/form_answer_qavs_with_la.json b/spec/fixtures/form_answer_qavs_with_la.json index 3a3fbd420..2bafd9811 100644 --- a/spec/fixtures/form_answer_qavs_with_la.json +++ b/spec/fixtures/form_answer_qavs_with_la.json @@ -113,6 +113,9 @@ } ], + "independent_individual": "yes", + "not_nominator": "yes", + "nominator_title": "Mr", "nominator_name": "Nominator", "nominator_address_building": "94", From 16834454070dfde6dcd05342ed63b681204ea615 Mon Sep 17 00:00:00 2001 From: Lubos Hricak Date: Thu, 18 Jul 2024 13:34:19 +0200 Subject: [PATCH 8/8] No need for a function --- .../frontend/custom_questions/support_letters.js.coffee | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee b/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee index a9665aece..af3e491b8 100644 --- a/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee +++ b/app/assets/javascripts/frontend/custom_questions/support_letters.js.coffee @@ -2,7 +2,8 @@ window.SupportLetters = init: -> $('.js-support-letter-attachment').each (idx, el) -> SupportLetters.fileupload_init(el) - SupportLetters.save_collection_init() + + $(document).on 'change', '.js-trigger-autosave', debounce(SupportLetters.submit, 1000) new_item_init: (el) -> SupportLetters.clean_up_system_tags(el) @@ -66,9 +67,6 @@ window.SupportLetters = hiddenInput = $('').prop('type', 'hidden').prop('name', prefixed) parent.append(hiddenInput) - save_collection_init: () -> - $(document).on 'change', '.js-trigger-autosave', debounce(SupportLetters.submit, 1000) - autosave: () -> url = $('form.qae-form').data('autosave-url') if url