feat(attestation): list tags errors and substitute missing tag by libelle
This commit is contained in:
parent
6f49dd892d
commit
f7484eb0e5
16 changed files with 139 additions and 55 deletions
|
@ -52,6 +52,8 @@ module Administrateurs
|
|||
['Redo', 'redo', 'arrow-go-forward-line']
|
||||
]
|
||||
]
|
||||
|
||||
@attestation_template.validate
|
||||
end
|
||||
|
||||
def update
|
||||
|
@ -67,15 +69,11 @@ module Administrateurs
|
|||
attestation_params[:signature] = uninterlace_png(signature_file)
|
||||
end
|
||||
|
||||
@attestation_template.update!(attestation_params)
|
||||
flash.notice = "Le modèle de l’attestation a été modifié"
|
||||
|
||||
respond_to do |format|
|
||||
format.turbo_stream
|
||||
format.html do
|
||||
redirect_to edit_admin_procedure_attestation_template_path(@procedure)
|
||||
end
|
||||
if !@attestation_template.update(attestation_params)
|
||||
flash.alert = "Le modèle de l’attestation contient des erreurs et n'a pas pu être enregistré. Corriger les erreurs."
|
||||
end
|
||||
|
||||
render :update
|
||||
end
|
||||
|
||||
def create = update
|
||||
|
|
|
@ -251,8 +251,15 @@ module TagsSubstitutionConcern
|
|||
}.reject { |_, ary| ary.empty? }
|
||||
end
|
||||
|
||||
def used_type_de_champ_tags(text)
|
||||
used_tags_and_libelle_for(text).filter_map do |(tag, libelle)|
|
||||
def used_type_de_champ_tags(text_or_tiptap)
|
||||
used_tags =
|
||||
if text_or_tiptap.respond_to?(:deconstruct_keys) # hash pattern matching
|
||||
TiptapService.new.used_tags_and_libelle_for(text_or_tiptap.deep_symbolize_keys)
|
||||
else
|
||||
used_tags_and_libelle_for(text_or_tiptap.to_s)
|
||||
end
|
||||
|
||||
used_tags.filter_map do |(tag, libelle)|
|
||||
if tag.nil?
|
||||
[libelle]
|
||||
elsif !tag.in?(SHARED_TAG_IDS) && tag.start_with?('tdc')
|
||||
|
@ -265,11 +272,11 @@ module TagsSubstitutionConcern
|
|||
used_tags_and_libelle_for(text).map { _1.first.nil? ? _1.second : _1.first }
|
||||
end
|
||||
|
||||
def tags_substitutions(tokens, dossier, escape: true)
|
||||
def tags_substitutions(tags_and_libelles, dossier, escape: true)
|
||||
# NOTE:
|
||||
# - tokens est un simple Set d'ids (pas la même structure que dans replace_tags)
|
||||
# - dans replace_tags, on fait référence à des tags avec ou sans id, mais pas ici,
|
||||
# a priori inutile car tiptap ne fait référence qu'aux ids.
|
||||
# - tags_and_libelles est un simple Set de couples (tag_id, libelle) (pas la même structure que dans replace_tags)
|
||||
# - dans `replace_tags`, on fait référence à des tags avec ou sans id, mais pas ici,
|
||||
# (inutile car tiptap ne référence que des ids)
|
||||
|
||||
@escape_unsafe_tags = escape
|
||||
|
||||
|
@ -283,12 +290,12 @@ module TagsSubstitutionConcern
|
|||
end
|
||||
end
|
||||
|
||||
tokens.index_with do |token|
|
||||
case flat_tags[token]
|
||||
tags_and_libelles.each_with_object({}) do |(tag_id, libelle), substitutions|
|
||||
substitutions[tag_id] = case flat_tags[tag_id]
|
||||
in tag, data
|
||||
replace_tag(tag, data)
|
||||
else
|
||||
token
|
||||
else # champ not in dossier, for example during preview on draft revision
|
||||
libelle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,13 +5,14 @@ class TiptapService
|
|||
children(node[:content], substitutions, 0)
|
||||
end
|
||||
|
||||
def used_tags(node, tags = Set.new)
|
||||
# NOTE: node must be deep symbolized keys
|
||||
def used_tags_and_libelle_for(node, tags = Set.new)
|
||||
case node
|
||||
in type: 'mention', attrs: { id: }
|
||||
tags << id
|
||||
in { content: } if content.is_a?(Array)
|
||||
content.each { used_tags(_1, tags) }
|
||||
else
|
||||
in type: 'mention', attrs: { id:, label: }, **rest
|
||||
tags << [id, label]
|
||||
in { content:, **rest } if content.is_a?(Array)
|
||||
content.each { used_tags_and_libelle_for(_1, tags) }
|
||||
in type:, **rest
|
||||
# noop
|
||||
end
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
class TagsValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
procedure = record.procedure
|
||||
tags = record.used_type_de_champ_tags(value.to_s)
|
||||
tags = record.used_type_de_champ_tags(value)
|
||||
|
||||
invalid_tags = tags.filter_map do |(tag, stable_id)|
|
||||
tag if stable_id.nil?
|
||||
end
|
||||
|
||||
invalid_for_draft_revision = invalid_tags_for_revision(record, attribute, tags, procedure.draft_revision)
|
||||
invalid_for_draft_revision = invalid_tags_for_revision(record, tags, procedure.draft_revision)
|
||||
|
||||
invalid_for_published_revision = if procedure.published_revision_id.present?
|
||||
invalid_tags_for_revision(record, attribute, tags, procedure.published_revision)
|
||||
invalid_tags_for_revision(record, tags, procedure.published_revision)
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ class TagsValidator < ActiveModel::EachValidator
|
|||
invalid_for_previous_revision = procedure
|
||||
.revisions_with_pending_dossiers
|
||||
.flat_map do |revision|
|
||||
invalid_tags_for_revision(record, attribute, tags, revision)
|
||||
invalid_tags_for_revision(record, tags, revision)
|
||||
end.uniq
|
||||
|
||||
# champ is added in draft revision but not yet published
|
||||
|
@ -48,7 +48,7 @@ class TagsValidator < ActiveModel::EachValidator
|
|||
end
|
||||
end
|
||||
|
||||
def invalid_tags_for_revision(record, attribute, tags, revision)
|
||||
def invalid_tags_for_revision(record, tags, revision)
|
||||
revision_stable_ids = revision
|
||||
.revision_types_de_champ
|
||||
.filter { !_1.child? }
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#autosave-notice.fr-badge.fr-badge--sm.fr-badge--success= t(".form_saved")
|
||||
- success = local_assigns.fetch(:success, true)
|
||||
#autosave-notice.fr-badge.fr-badge--sm{ class: class_names("fr-badge--success" => success, "fr-badge--error" => !success) }= success ? t(".form_saved") : t(".form_error")
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
= form_for @attestation_template, url: admin_procedure_attestation_template_v2_path(@procedure), html: { multipart: true },
|
||||
data: { turbo: 'true',
|
||||
controller: 'autosubmit attestation',
|
||||
autosubmit_debounce_delay_value: 2000,
|
||||
autosubmit_debounce_delay_value: 1000,
|
||||
attestation_logo_attachment_official_label_value: AttestationTemplate.human_attribute_name(:logo_additional),
|
||||
attestation_logo_attachment_free_label_value: AttestationTemplate.human_attribute_name(:logo) } do |f|
|
||||
|
||||
|
@ -65,12 +65,17 @@
|
|||
- c.with_hint { "Exemple: Direction interministérielle du numérique. 2 lignes maximum" }
|
||||
|
||||
.fr-fieldset__element.fr-mt-2w
|
||||
%label.fr-label.fr-h4
|
||||
= AttestationTemplate.human_attribute_name :body
|
||||
= render EditableChamp::AsteriskMandatoryComponent.new
|
||||
.fr-input-group{ class: class_names("fr-input-group--error" => f.object.errors.include?(:json_body)) }
|
||||
%label.fr-label.fr-h4
|
||||
= AttestationTemplate.human_attribute_name :body
|
||||
= render EditableChamp::AsteriskMandatoryComponent.new
|
||||
|
||||
.editor{ data: { tiptap_target: 'editor' } }
|
||||
= f.hidden_field :tiptap_body, data: { tiptap_target: 'input' }
|
||||
#editor.editor{ data: { tiptap_target: 'editor' }, aria: { describedby: dom_id(f.object, "json-body-messages")} }
|
||||
= f.hidden_field :tiptap_body, data: { tiptap_target: 'input' }
|
||||
|
||||
.fr-error-text{ id: dom_id(f.object, "json-body-messages"), class: class_names("hidden" => !f.object.errors.include?(:json_body)) }
|
||||
- if f.object.errors.include?(:json_body)
|
||||
= render partial: "shared/errors_list", locals: { object: f.object, attribute: :json_body }
|
||||
|
||||
.fr-fieldset__element
|
||||
.flex.flex-gap-2
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
= turbo_stream.show 'autosave-notice'
|
||||
= turbo_stream.replace 'autosave-notice', render(partial: 'administrateurs/autosave_notice')
|
||||
= turbo_stream.replace 'autosave-notice', render(partial: 'administrateurs/autosave_notice', locals: { success: !@attestation_template.changed? })
|
||||
= turbo_stream.hide 'autosave-notice', delay: 15000
|
||||
|
||||
- if @attestation_template.logo_blob&.previously_new_record?
|
||||
|
@ -9,3 +9,12 @@
|
|||
- if @attestation_template.signature_blob&.previously_new_record?
|
||||
= turbo_stream.update dom_id(@attestation_template, :signature_attachment) do
|
||||
= render(Attachment::EditComponent.new(attached_file: @attestation_template.signature, direct_upload: false))
|
||||
|
||||
- body_id = dom_id(@attestation_template, "json-body-messages")
|
||||
- if @attestation_template.errors.include?(:json_body)
|
||||
= turbo_stream.update body_id do
|
||||
= render partial: "shared/errors_list", locals: { object: @attestation_template, attribute: :json_body }
|
||||
= turbo_stream.show body_id
|
||||
- else
|
||||
= turbo_stream.hide body_id
|
||||
= turbo_stream.update body_id, nil
|
||||
|
|
3
app/views/shared/_errors_list.html.haml
Normal file
3
app/views/shared/_errors_list.html.haml
Normal file
|
@ -0,0 +1,3 @@
|
|||
%ul.list-style-type-none.fr-pl-0
|
||||
- object.errors.full_messages_for(attribute).map do |error_message|
|
||||
%li= error_message
|
Loading…
Add table
Add a link
Reference in a new issue