From 28e3d65e63b203f703493b86a4c02660017e937b Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Fri, 28 Mar 2025 21:36:26 +0200 Subject: [PATCH] improve required field handling --- app/controllers/submit_form_controller.rb | 4 ++ app/javascript/submission_form/form.vue | 8 +++- lib/submitters/submit_values.rb | 58 +++++++++++++++++------ 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/app/controllers/submit_form_controller.rb b/app/controllers/submit_form_controller.rb index 057a3e6f..fcd0fdc8 100644 --- a/app/controllers/submit_form_controller.rb +++ b/app/controllers/submit_form_controller.rb @@ -72,6 +72,10 @@ class SubmitFormController < ApplicationController Submitters::SubmitValues.call(submitter, params, request) head :ok + rescue Submitters::SubmitValues::RequiredFieldError => e + Rollbar.warning("Required field #{submitter.id}: #{e.message}") if defined?(Rollbar) + + render json: { field_uuid: e.message }, status: :unprocessable_entity rescue Submitters::SubmitValues::ValidationError => e render json: { error: e.message }, status: :unprocessable_entity end diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index e76ed805..cea04694 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -1404,7 +1404,13 @@ export default { if (response.status === 422 || response.status === 500) { const data = await response.json() - if (data.error) { + if (data.field_uuid) { + const field = this.fieldsUuidIndex[data.field_uuid] + + this.goToStep(this.stepFields.findIndex((fields) => fields.includes(field)), this.autoscrollFields) + + this.showFillAllRequiredFields = true + } else if (data.error) { const i18nKey = data.error.replace(/\s+/g, '_').toLowerCase() alert(this.t(i18nKey) !== i18nKey ? this.t(i18nKey) : data.error) diff --git a/lib/submitters/submit_values.rb b/lib/submitters/submit_values.rb index 15f2da02..446a4da1 100644 --- a/lib/submitters/submit_values.rb +++ b/lib/submitters/submit_values.rb @@ -3,6 +3,7 @@ module Submitters module SubmitValues ValidationError = Class.new(StandardError) + RequiredFieldError = Class.new(StandardError) VARIABLE_REGEXP = /\{\{?(\w+)\}\}?/ @@ -55,19 +56,26 @@ module Submitters submitter.ua = request.user_agent submitter.values = merge_default_values(submitter) - submitter.values = maybe_remove_condition_values(submitter) + + required_field_uuids_acc = Set.new + + submitter.values = maybe_remove_condition_values(submitter, required_field_uuids_acc:) formula_values = build_formula_values(submitter) if formula_values.present? submitter.values = submitter.values.merge(formula_values) - submitter.values = maybe_remove_condition_values(submitter) + submitter.values = maybe_remove_condition_values(submitter, required_field_uuids_acc:) end submitter.values = submitter.values.transform_values do |v| v == '{{date}}' ? Time.current.in_time_zone(submitter.account.timezone).to_date.to_s : v end + required_field_uuids_acc.each do |uuid| + raise RequiredFieldError, uuid if submitter.values[uuid].blank? + end + submitter end @@ -188,37 +196,59 @@ module Submitters with_time:) end - def maybe_remove_condition_values(submitter) + def maybe_remove_condition_values(submitter, required_field_uuids_acc: nil) fields_uuid_index = submitter.submission.template_fields.index_by { |e| e['uuid'] } - attachments_index = - Submissions.filtered_conditions_schema(submitter.submission).index_by { |i| i['attachment_uuid'] } + submitters_values = nil + has_other_submitters = submitter.submission.template_submitters.size > 1 + has_document_conditions = submitter.submission.template_schema.any? { |e| e['conditions'].present? } - submitter_values = nil - is_other_submitter_conditions = submitter.submission.template_submitters.size > 1 + attachments_index = + if has_document_conditions + Submissions.filtered_conditions_schema(submitter.submission).index_by { |i| i['attachment_uuid'] } + end submitter.submission.template_fields.each do |field| next if field['submitter_uuid'] != submitter.uuid - submitter_values ||= submitter.values + required_field_uuids_acc.add(field['uuid']) if required_field_uuids_acc && required_editable_field?(field) - is_other_submitter_conditions &&= field_conditions_other_submitter?(submitter, field, fields_uuid_index) - - if is_other_submitter_conditions - submitter_values = submitter.submission.submitters.reduce({}) { |acc, sub| acc.merge(sub.values) } + if has_document_conditions && check_field_areas_attachments(field, attachments_index) + submitter.values.delete(field['uuid']) + required_field_uuids_acc.delete(field['uuid']) end - submitter.values.delete(field['uuid']) unless check_field_conditions(submitter_values, field, fields_uuid_index) + if has_other_submitters && !submitters_values && + field_conditions_other_submitter?(submitter, field, fields_uuid_index) + submitters_values = merge_submitters_values(submitter) + end - if field['areas'].present? && field['areas'].none? { |area| attachments_index[area['attachment_uuid']] } + unless check_field_conditions(submitters_values || submitter.values, field, fields_uuid_index) submitter.values.delete(field['uuid']) + required_field_uuids_acc.delete(field['uuid']) end end submitter.values end + def required_editable_field?(field) + field['required'].present? && field['readonly'].blank? + end + + def check_field_areas_attachments(field, attachments_index) + field['areas'].present? && field['areas'].none? { |area| attachments_index[area['attachment_uuid']] } + end + + def merge_submitters_values(submitter) + submitter.submission.submitters + .reduce({}) { |acc, sub| acc.merge(sub.values) } + .merge(submitter.values) + end + def field_conditions_other_submitter?(submitter, field, fields_uuid_index) + return false if field['conditions'].blank? + field['conditions'].to_a.any? do |c| fields_uuid_index.dig(c['field_uuid'], 'submitter_uuid') != submitter.uuid end