From f5813b4e55c6d946ff393b80ad0ef8951e60c63d Mon Sep 17 00:00:00 2001 From: Christophe Robillard Date: Sun, 3 Mar 2024 11:30:41 +0100 Subject: [PATCH] create and update export templates --- .../export_templates_controller.rb | 82 ++++++++++++ app/models/export_template.rb | 8 +- .../export_templates/_form.html.haml | 59 +++++++++ .../export_templates/edit.html.haml | 7 + .../export_templates/new.html.haml | 6 + .../procedures/export_templates/fr.yml | 8 ++ config/routes.rb | 1 + .../export_templates_controller_spec.rb | 120 ++++++++++++++++++ 8 files changed, 288 insertions(+), 3 deletions(-) create mode 100644 app/controllers/instructeurs/export_templates_controller.rb create mode 100644 app/views/instructeurs/export_templates/_form.html.haml create mode 100644 app/views/instructeurs/export_templates/edit.html.haml create mode 100644 app/views/instructeurs/export_templates/new.html.haml create mode 100644 config/locales/views/instructeurs/procedures/export_templates/fr.yml create mode 100644 spec/controllers/instructeurs/export_templates_controller_spec.rb diff --git a/app/controllers/instructeurs/export_templates_controller.rb b/app/controllers/instructeurs/export_templates_controller.rb new file mode 100644 index 000000000..4b12a848b --- /dev/null +++ b/app/controllers/instructeurs/export_templates_controller.rb @@ -0,0 +1,82 @@ +module Instructeurs + class ExportTemplatesController < InstructeurController + before_action :set_procedure + before_action :set_groupe_instructeur, only: [:create, :update] + before_action :set_export_template, only: [:edit, :update, :destroy] + before_action :set_groupe_instructeurs + before_action :set_all_pj + + def new + @export_template = ExportTemplate.new(kind: 'zip', groupe_instructeur: @groupe_instructeurs.first) + @export_template.set_default_values + end + + def create + @export_template = @groupe_instructeur.export_templates.build(export_template_params) + @export_template.assign_pj_names(pj_params) + if @export_template.save + redirect_to exports_instructeur_procedure_path(procedure: @procedure), notice: "Le modèle d'export #{@export_template.name} a bien été créé" + else + flash[:alert] = @export_template.errors.full_messages + render :new + end + end + + def edit + end + + def update + @export_template.assign_attributes(export_template_params) + @export_template.groupe_instructeur = @groupe_instructeur + @export_template.assign_pj_names(pj_params) + if @export_template.save + redirect_to exports_instructeur_procedure_path(procedure: @procedure), notice: "Le modèle d'export #{@export_template.name} a bien été modifié" + else + flash[:alert] = @export_template.errors.full_messages + render :edit + end + end + + private + + def export_template_params + params.require(:export_template).permit(*export_params) + end + + def set_procedure + @procedure = current_instructeur.procedures.find params[:procedure_id] + Sentry.configure_scope do |scope| + scope.set_tags(procedure: @procedure.id) + end + end + + def set_export_template + @export_template = current_instructeur.export_templates.find(params[:id]) + end + + def set_groupe_instructeur + @groupe_instructeur = @procedure.groupe_instructeurs.find(params.require(:export_template)[:groupe_instructeur_id]) + end + + def set_groupe_instructeurs + @groupe_instructeurs = current_instructeur.groupe_instructeurs.where(procedure: @procedure) + end + + def set_all_pj + @all_pj ||= @procedure.pieces_jointes_exportables_list + end + + def export_params + [:name, :kind, :tiptap_default_dossier_directory, :tiptap_pdf_name] + end + + def pj_params + @procedure = current_instructeur.procedures.find params[:procedure_id] + pj_params = [] + @all_pj.each do |pj| + pj_params << "tiptap_pj_#{pj.stable_id}".to_sym + end + params.require(:export_template).permit(*pj_params) + end + end +end diff --git a/app/models/export_template.rb b/app/models/export_template.rb index 667b82b7e..2a7282fdc 100644 --- a/app/models/export_template.rb +++ b/app/models/export_template.rb @@ -37,9 +37,11 @@ class ExportTemplate < ApplicationRecord content_for_pj_id(pj.stable_id)&.to_json end - def content_for_pj_id(stable_id) - content_for_stable_id = content["pjs"].find { _1.symbolize_keys[:stable_id] == stable_id.to_s } - content_for_stable_id.symbolize_keys.fetch(:path) + def assign_pj_names(pj_params) + self.content["pjs"] = [] + pj_params.each do |pj_param| + self.content["pjs"] << { stable_id: pj_param[0].delete_prefix("tiptap_pj_"), path: JSON.parse(pj_param[1]) } + end end def attachment_and_path(dossier, attachment, index: 0, row_index: nil) diff --git a/app/views/instructeurs/export_templates/_form.html.haml b/app/views/instructeurs/export_templates/_form.html.haml new file mode 100644 index 000000000..1257f7f0d --- /dev/null +++ b/app/views/instructeurs/export_templates/_form.html.haml @@ -0,0 +1,59 @@ +.fr-grid-row.fr-grid-row--gutters + .fr-col-12.fr-col-md-8 + = form_with url: form_url, model: @export_template, local: true do |f| + = render Dsfr::InputComponent.new(form: f, attribute: :name, input_type: :text_field) do |c| + - c.with_hint do + Indiquez le nom à utiliser pour ce modèle d'export + + - if groupe_instructeurs.many? + .fr-input-group + = f.label :groupe_instructeur_id, class: 'fr-label' do + = f.object.class.human_attribute_name(:groupe_instructeur_id) + = render EditableChamp::AsteriskMandatoryComponent.new + %span.fr-hint-text + Avec quel groupe instructeur souhaitez-vous partager ce modèle d'export ? + = f.collection_select :groupe_instructeur_id, groupe_instructeurs, :id, :label, {}, class: 'fr-select' + - else + = f.hidden_field :groupe_instructeur_id + + = f.hidden_field :kind + + .fr-input-group{ data: { controller: 'tiptap' } } + = f.label :tiptap_default_dossier_directory, class: "fr-label" + .editor.mt-2{ data: { tiptap_target: 'editor' } } + = f.hidden_field :tiptap_default_dossier_directory, data: { tiptap_target: 'input' } + %ul.mt-2.flex.wrap.flex-gap-1 + - @export_template.specific_tags.each do |tag| + %li.fr-badge.fr-badge--sm{ role: 'button', title: tag[:description], data: { action: 'click->tiptap#insertTag', tiptap_target: 'tag', tag_id: tag[:id], tag_label: tag[:libelle] } } + = tag[:libelle] + + .fr-input-group{ data: { controller: 'tiptap' } } + = f.label :tiptap_pdf_name, class: "fr-label" + .editor.mt-2{ data: { tiptap_target: 'editor' } } + = f.hidden_field :tiptap_pdf_name, data: { tiptap_target: 'input' } + %ul.mt-2.flex.wrap.flex-gap-1 + - @export_template.specific_tags.each do |tag| + %li.fr-badge.fr-badge--sm{ role: 'button', title: tag[:description], data: { action: 'click->tiptap#insertTag', tiptap_target: 'tag', tag_id: tag[:id], tag_label: tag[:libelle] } } + = tag[:libelle] + + - if @all_pj.any? + %h3 Pieces justificatives + + - @all_pj.each do |pj| + .fr-input-group{ data: { controller: 'tiptap' } } + = label_tag pj.libelle, nil, name: field_name(:export_template, "tiptap_pj_#{pj.stable_id}"), class: "fr-label" + .editor.mt-2{ data: { tiptap_target: 'editor' } } + = hidden_field_tag field_name(:export_template, "tiptap_pj_#{pj.stable_id}"), "#{@export_template.content_for_pj(pj)}" , data: { tiptap_target: 'input' } + %ul.mt-2.flex.wrap.flex-gap-1 + - @export_template.specific_tags.each do |tag| + %li.fr-badge.fr-badge--sm{ role: 'button', title: tag[:description], data: { action: 'click->tiptap#insertTag', tiptap_target: 'tag', tag_id: tag[:id], tag_label: tag[:libelle] } } + = tag[:libelle] + + + .fixed-footer + .fr-container + %ul.fr-btns-group.fr-btns-group--inline-md + %li + = f.submit "Enregistrer", class: "fr-btn" + %li + = link_to "Annuler", instructeur_procedure_path(@procedure), class: "fr-btn fr-btn--secondary" diff --git a/app/views/instructeurs/export_templates/edit.html.haml b/app/views/instructeurs/export_templates/edit.html.haml new file mode 100644 index 000000000..bd4fc02b4 --- /dev/null +++ b/app/views/instructeurs/export_templates/edit.html.haml @@ -0,0 +1,7 @@ += render partial: 'administrateurs/breadcrumbs', + locals: { steps: [[@procedure.libelle.truncate_words(10), instructeur_procedure_path(@procedure)], + [t('.title')]] } +.fr-container + %h1 Mise à jour modèle d'export + + = render partial: 'form', locals: { form_url: instructeur_export_template_path(@procedure, @export_template), groupe_instructeurs: @groupe_instructeurs } diff --git a/app/views/instructeurs/export_templates/new.html.haml b/app/views/instructeurs/export_templates/new.html.haml new file mode 100644 index 000000000..eeff6baa9 --- /dev/null +++ b/app/views/instructeurs/export_templates/new.html.haml @@ -0,0 +1,6 @@ += render partial: 'administrateurs/breadcrumbs', + locals: { steps: [[@procedure.libelle.truncate_words(10), instructeur_procedure_path(@procedure)], + [t('.title')]] } +.fr-container + %h1 Nouveau modèle d'export + = render partial: 'form', locals: { form_url: instructeur_export_templates_path, groupe_instructeurs: @groupe_instructeurs } diff --git a/config/locales/views/instructeurs/procedures/export_templates/fr.yml b/config/locales/views/instructeurs/procedures/export_templates/fr.yml new file mode 100644 index 000000000..6f570152e --- /dev/null +++ b/config/locales/views/instructeurs/procedures/export_templates/fr.yml @@ -0,0 +1,8 @@ +fr: + instructeurs: + export_templates: + new: + title: Nouveau modèle d'export + edit: + title: Modèle d'export + diff --git a/config/routes.rb b/config/routes.rb index cb068da33..a9af9ac9e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -450,6 +450,7 @@ Rails.application.routes.draw do resources :procedures, only: [:index, :show], param: :procedure_id do member do resources :archives, only: [:index, :create] + resources :export_templates, only: [:new, :create, :edit, :update] resources :groupes, only: [:index, :show], controller: 'groupe_instructeurs' do resource :contact_information diff --git a/spec/controllers/instructeurs/export_templates_controller_spec.rb b/spec/controllers/instructeurs/export_templates_controller_spec.rb new file mode 100644 index 000000000..83adb32ac --- /dev/null +++ b/spec/controllers/instructeurs/export_templates_controller_spec.rb @@ -0,0 +1,120 @@ +describe Instructeurs::ExportTemplatesController, type: :controller do + before { sign_in(instructeur.user) } + let(:tiptap_pdf_name) { + { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "mon_export_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }] } + ] + }.to_json + } + + let(:export_template_params) do + { + name: "coucou", + kind: "zip", + groupe_instructeur_id: groupe_instructeur.id, + tiptap_pdf_name: tiptap_pdf_name, + tiptap_default_dossier_directory: { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "DOSSIER_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, { "text" => " ", "type" => "text" }] } + ] + }.to_json, + "pjs" => + [ + { path: { "type" => "doc", "content" => [{ "type" => "paragraph", "content" => [{ "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, { "text" => " _justif", "type" => "text" }] }] }, stable_id: "3" }, + { + path: + { "type" => "doc", "content" => [{ "type" => "paragraph", "content" => [{ "text" => "cni_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, { "text" => " ", "type" => "text" }] }] }, + stable_id: "5" + }, + { + path: { "type" => "doc", "content" => [{ "type" => "paragraph", "content" => [{ "text" => "pj_repet_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }, { "text" => " ", "type" => "text" }] }] }, + stable_id: "10" + } + ] + } + end + + let(:instructeur) { create(:instructeur) } + let(:procedure) { create(:procedure, instructeurs: [instructeur]) } + let(:groupe_instructeur) { procedure.defaut_groupe_instructeur } + + describe '#create' do + let(:subject) { post :create, params: { procedure_id: procedure.id, export_template: export_template_params } } + + context 'with valid params' do + it 'redirect to some page' do + subject + expect(response).to redirect_to(exports_instructeur_procedure_path(procedure:)) + expect(flash.notice).to eq "Le modèle d'export coucou a bien été créé" + end + end + + context 'with invalid params' do + let(:tiptap_pdf_name) { { content: "invalid" }.to_json } + it 'display error notification' do + subject + expect(flash.alert).to be_present + end + end + + context 'with procedure not accessible by current instructeur' do + let(:another_procedure) { create(:procedure) } + let(:subject) { post :create, params: { procedure_id: another_procedure.id, export_template: export_template_params } } + it 'raise exception' do + expect { subject }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + + describe '#edit' do + let(:export_template) { create(:export_template, groupe_instructeur:) } + let(:subject) { get :edit, params: { procedure_id: procedure.id, id: export_template.id } } + + it 'render edit' do + subject + expect(response).to render_template(:edit) + end + + context "with export_template not accessible by current instructeur" do + let(:another_groupe_instructeur) { create(:groupe_instructeur) } + let(:export_template) { create(:export_template, groupe_instructeur: another_groupe_instructeur) } + + it 'raise exception' do + expect { subject }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + + describe '#update' do + let(:export_template) { create(:export_template, groupe_instructeur:) } + let(:tiptap_pdf_name) { + { + "type" => "doc", + "content" => [ + { "type" => "paragraph", "content" => [{ "text" => "exPort_", "type" => "text" }, { "type" => "mention", "attrs" => { "id" => "dossier_number", "label" => "numéro du dossier" } }] } + ] + }.to_json + } + + let(:subject) { put :update, params: { procedure_id: procedure.id, id: export_template.id, export_template: export_template_params } } + + context 'with valid params' do + it 'redirect to some page' do + subject + expect(response).to redirect_to(exports_instructeur_procedure_path(procedure:)) + expect(flash.notice).to eq "Le modèle d'export coucou a bien été modifié" + end + end + + context 'with invalid params' do + let(:tiptap_pdf_name) { { content: "invalid" }.to_json } + it 'display error notification' do + subject + expect(flash.alert).to be_present + end + end + end +end