commit
58068b13b9
18 changed files with 79 additions and 63 deletions
|
@ -1,8 +1,14 @@
|
||||||
class Champs::RepetitionController < ApplicationController
|
class Champs::RepetitionController < ApplicationController
|
||||||
before_action :authenticate_logged_user!
|
before_action :authenticate_logged_user!
|
||||||
|
|
||||||
def show
|
def add
|
||||||
@champ = policy_scope(Champ).includes(:champs).find(params[:champ_id])
|
@champ = policy_scope(Champ).includes(:champs).find(params[:champ_id])
|
||||||
@champs = @champ.add_row
|
@champs = @champ.add_row
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def remove
|
||||||
|
champ = policy_scope(Champ).includes(:champs).find(params[:champ_id])
|
||||||
|
champ.champs.where(id: params[:champ_ids]).destroy_all
|
||||||
|
@row_id = params[:row_id]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
import { delegate, fire } from '@utils';
|
|
||||||
|
|
||||||
const CHAMP_SELECTOR = '.editable-champ';
|
|
||||||
const BUTTON_SELECTOR = '.button.remove-row';
|
|
||||||
const DESTROY_INPUT_SELECTOR = 'input[type=hidden][name*=_destroy]';
|
|
||||||
const DOM_ID_INPUT_SELECTOR = 'input[type=hidden][name*=deleted_row_dom_ids]';
|
|
||||||
|
|
||||||
delegate('click', BUTTON_SELECTOR, (evt) => {
|
|
||||||
evt.preventDefault();
|
|
||||||
|
|
||||||
const row = evt.target.closest('.row');
|
|
||||||
|
|
||||||
for (let input of row.querySelectorAll(DESTROY_INPUT_SELECTOR)) {
|
|
||||||
input.disabled = false;
|
|
||||||
input.value = true;
|
|
||||||
}
|
|
||||||
row.querySelector(DOM_ID_INPUT_SELECTOR).disabled = false;
|
|
||||||
|
|
||||||
for (let champ of row.querySelectorAll(CHAMP_SELECTOR)) {
|
|
||||||
champ.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
evt.target.remove();
|
|
||||||
row.classList.remove('row');
|
|
||||||
|
|
||||||
// We could debounce the autosave request, so that row removal would be batched
|
|
||||||
// with the next changes.
|
|
||||||
// However *adding* a new repetition row isn't debounced (changes are immediately
|
|
||||||
// effective server-side).
|
|
||||||
// So, to avoid ordering issues, enqueue an autosave request as soon as the row
|
|
||||||
// is removed.
|
|
||||||
fire(row, 'autosave:trigger');
|
|
||||||
});
|
|
|
@ -31,7 +31,6 @@ import '../new_design/dossiers/auto-save';
|
||||||
import '../new_design/dossiers/auto-upload';
|
import '../new_design/dossiers/auto-upload';
|
||||||
|
|
||||||
import '../new_design/champs/linked-drop-down-list';
|
import '../new_design/champs/linked-drop-down-list';
|
||||||
import '../new_design/champs/repetition';
|
|
||||||
import '../new_design/champs/drop-down-list';
|
import '../new_design/champs/drop-down-list';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -138,7 +138,7 @@ export function httpRequest(
|
||||||
controller?: AbortController;
|
controller?: AbortController;
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
const headers = new Headers(init.headers);
|
const headers = init.headers ? new Headers(init.headers) : new Headers();
|
||||||
if (csrf) {
|
if (csrf) {
|
||||||
headers.set('x-csrf-token', csrfToken() ?? '');
|
headers.set('x-csrf-token', csrfToken() ?? '');
|
||||||
headers.set('x-requested-with', 'XMLHttpRequest');
|
headers.set('x-requested-with', 'XMLHttpRequest');
|
||||||
|
|
2
app/views/champs/repetition/add.turbo_stream.haml
Normal file
2
app/views/champs/repetition/add.turbo_stream.haml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
= fields_for @champ.input_name, @champ do |form|
|
||||||
|
= turbo_stream.append dom_id(@champ, :rows), partial: 'shared/dossiers/editable_champs/repetition_row', locals: { form: form, champ: @champ, row: @champs }
|
1
app/views/champs/repetition/remove.turbo_stream.haml
Normal file
1
app/views/champs/repetition/remove.turbo_stream.haml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
= turbo_stream.remove @row_id
|
|
@ -1,3 +0,0 @@
|
||||||
<%= fields_for @champ.input_name, @champ do |form| %>
|
|
||||||
<%= append_to_element("##{@champ.input_group_id} .repetition", partial: 'shared/dossiers/editable_champs/repetition_row', locals: { form: form, champ: @champ, row: @champs }) %>
|
|
||||||
<% end %>
|
|
|
@ -1,9 +1,20 @@
|
||||||
require 'prawn/measurement_extensions'
|
require 'prawn/measurement_extensions'
|
||||||
|
|
||||||
|
# Render text in a box that expands vertically, then move the cursor down to the end of the rendered text
|
||||||
|
def render_expanding_text_box(pdf, text, options)
|
||||||
|
box = Prawn::Text::Box.new(text, options.merge(document: pdf, overflow: :expand))
|
||||||
|
|
||||||
|
box.render(dry_run: true)
|
||||||
|
vertical_space_used = box.height
|
||||||
|
|
||||||
|
box.render
|
||||||
|
pdf.move_down(vertical_space_used)
|
||||||
|
end
|
||||||
|
|
||||||
def render_in_2_columns(pdf, label, text)
|
def render_in_2_columns(pdf, label, text)
|
||||||
pdf.text_box label, width: 200, height: 100, overflow: :expand, at: [0, pdf.cursor]
|
pdf.text_box label, width: 200, height: 100, overflow: :expand, at: [0, pdf.cursor]
|
||||||
pdf.text_box ":", width: 10, height: 100, overflow: :expand, at: [100, pdf.cursor]
|
pdf.text_box ":", width: 10, height: 100, overflow: :expand, at: [100, pdf.cursor]
|
||||||
pdf.text_box text, width: 420, height: 100, overflow: :expand, at: [110, pdf.cursor]
|
render_expanding_text_box(pdf, text, width: 420, height: 100, at: [110, pdf.cursor])
|
||||||
pdf.text "\n"
|
pdf.text "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -26,10 +37,24 @@ def format_in_2_columns(pdf, label)
|
||||||
pdf.text "\n"
|
pdf.text "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
def format_with_checkbox(pdf, label, offset = 0)
|
def format_with_checkbox(pdf, option, offset = 0)
|
||||||
|
# Option is a [text, value] pair, or a string used for both.
|
||||||
|
label = option.is_a?(String) ? option : option.first
|
||||||
|
value = option.is_a?(String) ? option : option.last
|
||||||
|
|
||||||
|
if value == Champs::DropDownListChamp::OTHER
|
||||||
|
label += " : "
|
||||||
|
end
|
||||||
|
|
||||||
pdf.font 'marianne', size: 9 do
|
pdf.font 'marianne', size: 9 do
|
||||||
pdf.stroke_rectangle [0 + offset, pdf.cursor], 10, 10
|
pdf.stroke_rectangle [0 + offset, pdf.cursor], 10, 10
|
||||||
pdf.text_box label, at: [15 + offset, pdf.cursor]
|
pdf.text_box label, at: [15 + offset, pdf.cursor - 1]
|
||||||
|
|
||||||
|
if value == Champs::DropDownListChamp::OTHER
|
||||||
|
pdf.bounding_box([110, pdf.cursor + 3],:width => 350,:height => 20) do
|
||||||
|
pdf.stroke_bounds
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
pdf.text "\n"
|
pdf.text "\n"
|
||||||
end
|
end
|
||||||
|
@ -199,7 +224,7 @@ prawn_document(page_size: "A4") do |pdf|
|
||||||
pdf.text "\n"
|
pdf.text "\n"
|
||||||
|
|
||||||
add_title(pdf, 'Formulaire')
|
add_title(pdf, 'Formulaire')
|
||||||
add_single_line(pdf, @procedure.description + "\n", 9, :italic) if @procedure.description.present?
|
add_single_line(pdf, @dossier.procedure.description + "\n", 9, :italic) if @dossier.procedure.description.present?
|
||||||
add_champs(pdf, @dossier.champs)
|
add_champs(pdf, @dossier.champs)
|
||||||
add_page_numbering(pdf)
|
add_page_numbering(pdf)
|
||||||
add_procedure(pdf, @dossier)
|
add_procedure(pdf, @dossier)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#dossier-annotations-privees.container
|
#dossier-annotations-privees.container
|
||||||
- if @dossier.champs_private.present?
|
- if @dossier.champs_private.present?
|
||||||
%section
|
%section
|
||||||
= form_for @dossier, url: annotations_instructeur_dossier_path(@dossier.procedure, @dossier), html: { class: 'form' } do |f|
|
= form_for @dossier, url: annotations_instructeur_dossier_path(@dossier.procedure, @dossier), html: { class: 'form', multipart: true } do |f|
|
||||||
- @dossier.champs_private.each do |champ|
|
- @dossier.champs_private.each do |champ|
|
||||||
= fields_for champ.input_name, champ do |form|
|
= fields_for champ.input_name, champ do |form|
|
||||||
= render partial: "shared/dossiers/editable_champs/editable_champ", locals: { form: form, champ: champ, seen_at: @annotations_privees_seen_at }
|
= render partial: "shared/dossiers/editable_champs/editable_champ", locals: { form: form, champ: champ, seen_at: @annotations_privees_seen_at }
|
||||||
|
|
|
@ -42,4 +42,8 @@
|
||||||
|
|
||||||
= yield :charts_js
|
= yield :charts_js
|
||||||
|
|
||||||
|
// Container for custom turbo-stream actions
|
||||||
%turbo-events
|
%turbo-events
|
||||||
|
// Container for turbo form that we can submit from inside other forms
|
||||||
|
%div{ 'data-turbo': 'true' }
|
||||||
|
= form_tag('', id: :turbo_form)
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
.repetition
|
.repetition{ id: dom_id(champ, :rows) }
|
||||||
- champ.rows.each do |champs|
|
- champ.rows.each do |champs|
|
||||||
= render partial: 'shared/dossiers/editable_champs/repetition_row', locals: { form: form, champ: champ, row: champs }
|
= render partial: 'shared/dossiers/editable_champs/repetition_row', locals: { form: form, champ: champ, row: champs }
|
||||||
|
|
||||||
- if champ.persisted?
|
.actions{ 'data-turbo': 'true' }
|
||||||
= link_to champs_repetition_path(champ.id), class: 'button add-row', data: { remote: true, disable: true, method: 'POST' } do
|
= button_tag type: :submit, form: :turbo_form, formaction: champs_repetition_path(champ.id), formmethod: :post, class: 'button add-row' do
|
||||||
%span.icon.add
|
|
||||||
Ajouter un élément pour « #{champ.libelle} »
|
|
||||||
- else
|
|
||||||
%a.button.add-row
|
|
||||||
%span.icon.add
|
%span.icon.add
|
||||||
Ajouter un élément pour « #{champ.libelle} »
|
Ajouter un élément pour « #{champ.libelle} »
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
- row_dom_id = "row-#{SecureRandom.hex(4)}"
|
- row_dom_id = "row-#{SecureRandom.hex(4)}"
|
||||||
.row{ id: row_dom_id }
|
.row{ id: row_dom_id }
|
||||||
-# Tell the controller which DOM element should be removed once the row deletion is successful
|
|
||||||
= hidden_field_tag 'deleted_row_dom_ids[]', row_dom_id, disabled: true
|
|
||||||
|
|
||||||
- row.each do |champ|
|
- row.each do |champ|
|
||||||
= fields_for champ.input_name, champ do |form|
|
= fields_for champ.input_name, champ do |form|
|
||||||
= render partial: 'shared/dossiers/editable_champs/editable_champ', locals: { form: form, champ: champ }
|
= render partial: 'shared/dossiers/editable_champs/editable_champ', locals: { form: form, champ: champ }
|
||||||
= form.hidden_field :_destroy, disabled: true
|
|
||||||
|
|
||||||
.flex.row-reverse
|
.flex.row-reverse{ 'data-turbo': 'true' }
|
||||||
%button.button.danger.remove-row{ type: :button }
|
= button_tag type: :submit, form: :turbo_form, formaction: champs_repetition_path(champ.id, champ_ids: row.map(&:id), row_id: row_dom_id), formmethod: :delete, class: 'button danger remove-row' do
|
||||||
Supprimer l’élément
|
Supprimer l’élément
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<% (params['deleted_row_dom_ids'] || []).each do |deleted_row_dom_id| %>
|
|
||||||
<%= remove_element('#' + deleted_row_dom_id) %>
|
|
||||||
<% end %>
|
|
|
@ -42,7 +42,7 @@ prawn_document(margin: [top_margin, right_margin, bottom_margin, left_margin], p
|
||||||
pdf.pad_top(40) { pdf.text @dossier.procedure.libelle, size: 14, character_spacing: -0.2, align: :center }
|
pdf.pad_top(40) { pdf.text @dossier.procedure.libelle, size: 14, character_spacing: -0.2, align: :center }
|
||||||
|
|
||||||
pdf.fill_color grey
|
pdf.fill_color grey
|
||||||
description = t('.description', user_name: papertrail_requester_identity(@dossier), procedure: @dossier.procedure.libelle, date: l(@dossier.created_at, format: '%e %B %Y'))
|
description = t('.description', user_name: papertrail_requester_identity(@dossier), procedure: @dossier.procedure.libelle, date: l(@dossier.depose_at, format: '%e %B %Y'))
|
||||||
pdf.pad_top(30) { pdf.text description, size: 10, character_spacing: -0.2, align: :left }
|
pdf.pad_top(30) { pdf.text description, size: 10, character_spacing: -0.2, align: :left }
|
||||||
|
|
||||||
pdf.fill_color black
|
pdf.fill_color black
|
||||||
|
@ -70,7 +70,7 @@ prawn_document(margin: [top_margin, right_margin, bottom_margin, left_margin], p
|
||||||
pdf.fill_color grey
|
pdf.fill_color grey
|
||||||
pdf.pad_top(7) do
|
pdf.pad_top(7) do
|
||||||
pdf.text "#{Dossier.human_attribute_name(:id)} : #{@dossier.id.to_s}", size: 10, character_spacing: -0.2, align: :justify
|
pdf.text "#{Dossier.human_attribute_name(:id)} : #{@dossier.id.to_s}", size: 10, character_spacing: -0.2, align: :justify
|
||||||
pdf.text t('.file_submitted_at') + ' : ' + l(@dossier.en_construction_at, format: '%e %B %Y'), size: 10, character_spacing: -0.2, align: :justify
|
pdf.text t('.file_submitted_at') + ' : ' + l(@dossier.depose_at, format: '%e %B %Y'), size: 10, character_spacing: -0.2, align: :justify
|
||||||
pdf.text t('.dossier_state') + ' : ' + papertrail_dossier_state(@dossier), size: 10, character_spacing: -0.2, align: :justify
|
pdf.text t('.dossier_state') + ' : ' + papertrail_dossier_state(@dossier), size: 10, character_spacing: -0.2, align: :justify
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,8 @@ Rails.application.routes.draw do
|
||||||
get ':champ_id/siret', to: 'siret#show', as: :siret
|
get ':champ_id/siret', to: 'siret#show', as: :siret
|
||||||
get ':champ_id/dossier_link', to: 'dossier_link#show', as: :dossier_link
|
get ':champ_id/dossier_link', to: 'dossier_link#show', as: :dossier_link
|
||||||
post ':champ_id/carte', to: 'carte#show', as: :carte
|
post ':champ_id/carte', to: 'carte#show', as: :carte
|
||||||
post ':champ_id/repetition', to: 'repetition#show', as: :repetition
|
post ':champ_id/repetition', to: 'repetition#add', as: :repetition
|
||||||
|
delete ':champ_id/repetition', to: 'repetition#remove'
|
||||||
|
|
||||||
get ':champ_id/carte/features', to: 'carte#index', as: :carte_features
|
get ':champ_id/carte/features', to: 'carte#index', as: :carte_features
|
||||||
post ':champ_id/carte/features', to: 'carte#create'
|
post ':champ_id/carte/features', to: 'carte#create'
|
||||||
|
|
|
@ -202,6 +202,12 @@ FactoryBot.define do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trait :with_drop_down_list do
|
||||||
|
after(:build) do |procedure, _evaluator|
|
||||||
|
build(:type_de_champ_drop_down_list, :with_other, procedure: procedure)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
trait :with_address do
|
trait :with_address do
|
||||||
after(:build) do |procedure, _evaluator|
|
after(:build) do |procedure, _evaluator|
|
||||||
build(:type_de_champ_address, procedure: procedure)
|
build(:type_de_champ_address, procedure: procedure)
|
||||||
|
|
|
@ -92,6 +92,9 @@ FactoryBot.define do
|
||||||
trait :without_selectable_values do
|
trait :without_selectable_values do
|
||||||
drop_down_list_value { "\r\n--separateur--\r\n--separateur 2--\r\n \r\n" }
|
drop_down_list_value { "\r\n--separateur--\r\n--separateur 2--\r\n \r\n" }
|
||||||
end
|
end
|
||||||
|
trait :with_other do
|
||||||
|
drop_down_other { true }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
factory :type_de_champ_multiple_drop_down_list do
|
factory :type_de_champ_multiple_drop_down_list do
|
||||||
type_champ { TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) }
|
type_champ { TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) }
|
||||||
|
|
16
spec/views/dossiers/dossier_vide.pdf.prawn_spec.rb
Normal file
16
spec/views/dossiers/dossier_vide.pdf.prawn_spec.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
describe 'dossiers/dossier_vide.pdf.prawn', type: :view do
|
||||||
|
let(:procedure) { create(:procedure, :with_all_champs, :with_drop_down_list) }
|
||||||
|
let(:dossier) { create(:dossier, procedure: procedure) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
assign(:procedure, procedure)
|
||||||
|
assign(:dossier, dossier)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { render }
|
||||||
|
|
||||||
|
it 'renders a PDF document with empty fields' do
|
||||||
|
subject
|
||||||
|
expect(rendered).to be_present
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue