diff --git a/app/assets/stylesheets/new_design/buttons.scss b/app/assets/stylesheets/new_design/buttons.scss
index 5092221ff..8597e720a 100644
--- a/app/assets/stylesheets/new_design/buttons.scss
+++ b/app/assets/stylesheets/new_design/buttons.scss
@@ -100,6 +100,11 @@
}
}
+ &.small {
+ line-height: 14px;
+ padding: 5px 10px 6px 10px;
+ }
+
&.large {
font-size: 18px;
line-height: 26px;
diff --git a/app/assets/stylesheets/new_design/editable_champs/piece_justificative.scss b/app/assets/stylesheets/new_design/editable_champs/piece_justificative.scss
new file mode 100644
index 000000000..906f9dc6c
--- /dev/null
+++ b/app/assets/stylesheets/new_design/editable_champs/piece_justificative.scss
@@ -0,0 +1,18 @@
+@import "../constants";
+
+.piece-justificative-actions {
+ display: flex;
+ margin-bottom: $default-spacer;
+}
+
+.piece-justificative-action {
+ margin-right: $default-spacer;
+
+ .button {
+ text-transform: lowercase;
+ }
+}
+
+.piece-justificative-input.hidden {
+ display: none;
+}
diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss
index 0e6954664..58547b25d 100644
--- a/app/assets/stylesheets/new_design/forms.scss
+++ b/app/assets/stylesheets/new_design/forms.scss
@@ -100,9 +100,9 @@
input[type=date],
input[type=number],
input[type=tel],
- input[type=file],
textarea,
- select {
+ select,
+ .piece-justificative {
display: block;
margin-bottom: 2 * $default-padding;
@@ -111,10 +111,6 @@
}
}
- .direct-upload {
- margin-bottom: 2 * $default-padding;
- }
-
.add-row {
margin-bottom: 2 * $default-padding;
}
diff --git a/app/controllers/champs/carte_controller.rb b/app/controllers/champs/carte_controller.rb
index 5374c5111..d214f30d0 100644
--- a/app/controllers/champs/carte_controller.rb
+++ b/app/controllers/champs/carte_controller.rb
@@ -13,19 +13,16 @@ class Champs::CarteController < ApplicationController
coordinates = params[:dossier][:champs_private_attributes][params[:position]][:value]
end
- if params[:champ_id].present?
- @champ = Champ
+ @champ = if params[:champ_id].present?
+ Champ
.joins(:dossier)
.where(dossiers: { user_id: logged_user_ids })
.find(params[:champ_id])
else
- @champ = Champs::CarteChamp.new(type_de_champ: TypeDeChamp.new(
- type_champ: TypeDeChamp.type_champs.fetch(:carte),
- options: {
- quartiers_prioritaires: true,
- cadastres: true
- }
- ))
+ TypeDeChamp
+ .joins(:procedure)
+ .where(procedures: { administrateur_id: logged_user_ids })
+ .find(params[:type_de_champ_id]).champ.build
end
geo_areas = []
diff --git a/app/dashboards/dossier_dashboard.rb b/app/dashboards/dossier_dashboard.rb
index 456b3f3a4..698106704 100644
--- a/app/dashboards/dossier_dashboard.rb
+++ b/app/dashboards/dossier_dashboard.rb
@@ -16,7 +16,7 @@ class DossierDashboard < Administrate::BaseDashboard
created_at: Field::DateTime,
updated_at: Field::DateTime,
hidden_at: Field::DateTime,
- types_de_champ: TypesDeChampCollectionField
+ champs: ChampCollectionField
}.freeze
# COLLECTION_ATTRIBUTES
@@ -38,7 +38,7 @@ class DossierDashboard < Administrate::BaseDashboard
:state,
:procedure,
:user,
- :types_de_champ,
+ :champs,
:created_at,
:updated_at,
:hidden_at
diff --git a/app/fields/champ_collection_field.rb b/app/fields/champ_collection_field.rb
new file mode 100644
index 000000000..ed6dbe634
--- /dev/null
+++ b/app/fields/champ_collection_field.rb
@@ -0,0 +1,7 @@
+require "administrate/field/base"
+
+class ChampCollectionField < Administrate::Field::Base
+ def to_s
+ data
+ end
+end
diff --git a/app/helpers/champ_helper.rb b/app/helpers/champ_helper.rb
index 573a8defe..ba376d8bb 100644
--- a/app/helpers/champ_helper.rb
+++ b/app/helpers/champ_helper.rb
@@ -9,4 +9,12 @@ module ChampHelper
raw(champ.to_render_data.to_json)
# rubocop:enable Rails/OutputSafety
end
+
+ def champ_carte_params(champ)
+ if champ.persisted?
+ { champ_id: champ.id }
+ else
+ { type_de_champ_id: champ.type_de_champ_id }
+ end
+ end
end
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index df838bc5a..8bac1343f 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -12,6 +12,7 @@ import '../shared/safari-11-file-xhr-workaround';
import '../shared/autocomplete';
import '../shared/remote-input';
import '../shared/franceconnect';
+import '../shared/toggle-target';
import '../new_design/spinner';
import '../new_design/dropdown';
diff --git a/app/javascript/shared/toggle-target.js b/app/javascript/shared/toggle-target.js
new file mode 100644
index 000000000..0b4faf0f5
--- /dev/null
+++ b/app/javascript/shared/toggle-target.js
@@ -0,0 +1,20 @@
+import { delegate, toggle } from '@utils';
+
+// Unobtrusive Javascript for allowing an element to toggle
+// the visibility of another element.
+//
+// Usage:
+//
+//
Content
+
+const TOGGLE_SOURCE_SELECTOR = '[data-toggle-target]';
+
+delegate('click', TOGGLE_SOURCE_SELECTOR, evt => {
+ evt.preventDefault();
+
+ const targetSelector = evt.target.dataset.toggleTarget;
+ const targetElements = document.querySelectorAll(targetSelector);
+ for (let target of targetElements) {
+ toggle(target);
+ }
+});
diff --git a/app/models/champ.rb b/app/models/champ.rb
index e0d7b35f1..c88e0fe3f 100644
--- a/app/models/champ.rb
+++ b/app/models/champ.rb
@@ -23,15 +23,15 @@ class Champ < ApplicationRecord
end
def mandatory_and_blank?
- if mandatory?
- case type_de_champ.type_champ
- when TypeDeChamp.type_champs.fetch(:carte)
- value.blank? || value == '[]'
- else
- value.blank?
- end
+ mandatory? && blank?
+ end
+
+ def blank?
+ case type_de_champ.type_champ
+ when TypeDeChamp.type_champs.fetch(:carte)
+ value.blank? || value == '[]'
else
- false
+ value.blank?
end
end
diff --git a/app/views/admin/procedures/show.html.haml b/app/views/admin/procedures/show.html.haml
index 34408d6a7..b51fcde0e 100644
--- a/app/views/admin/procedures/show.html.haml
+++ b/app/views/admin/procedures/show.html.haml
@@ -65,7 +65,7 @@
pour y accéder vous pouvez utiliser le lien :
= link_to procedure_lien(@procedure), sanitize_url(procedure_lien(@procedure)), target: :blank
%p
- Tout personne ayant la connaissance de ce lien pourra ainsi remplir des dossiers sur votre démarche.
+ Tout personne ayant la connaissance de ce lien pourra ainsi remplir des dossiers de test sur votre démarche.
%br
%h4 Ce que vous pouvez faire lorsque vous êtes en test
diff --git a/app/views/fields/champ_collection_field/_show.html.haml b/app/views/fields/champ_collection_field/_show.html.haml
new file mode 100644
index 000000000..f5528c9e9
--- /dev/null
+++ b/app/views/fields/champ_collection_field/_show.html.haml
@@ -0,0 +1,24 @@
+- if field.data.any?
+ %table.collection-data{ "aria-labelledby": "page-title" }
+ %thead
+ %tr
+ %td.cell-label Libelle
+ %td.cell-label Type de champ
+ %td.cell-label Rempli
+ %tbody
+ - field.data.each do |f|
+ %tr
+ %td.cell-data
+ = f.libelle
+ - if f.mandatory?
+ %span.mandatory{ style: 'color: #A10005;' } *
+ %td.cell-data
+ = I18n.t("activerecord.attributes.type_de_champ.type_champs.#{f.type_champ}")
+
+ %td.cell-data
+ - if f.blank?
+ vide
+ - else
+ rempli
+- else
+ Aucun
diff --git a/app/views/fields/types_de_champ_collection_field/_show.html.haml b/app/views/fields/types_de_champ_collection_field/_show.html.haml
index c54bb1c64..86b298e54 100644
--- a/app/views/fields/types_de_champ_collection_field/_show.html.haml
+++ b/app/views/fields/types_de_champ_collection_field/_show.html.haml
@@ -4,12 +4,21 @@
%tr
%td.cell-label Libelle
%td.cell-label Type de champ
+ %td.cell-label Rempli
%tbody
- field.data.order(:order_place).each do |f|
%tr
%td.cell-data
= f.libelle
+ - if f.mandatory?
+ %span.mandatory{ style: 'color: #A10005;' } *
%td.cell-data
= I18n.t("activerecord.attributes.type_de_champ.type_champs.#{f.type_champ}")
+
+ %td.cell-data
+ - if f.blank?
+ vide
+ - else
+ rempli
- else
Aucun
diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml
index 7ce15a032..331b30681 100644
--- a/app/views/layouts/_footer.html.haml
+++ b/app/views/layouts/_footer.html.haml
@@ -10,3 +10,7 @@
= link_to 'CGU / Mentions légales', CGU_URL
\-
= contact_link 'Contact'
+ \-
+ = link_to 'FAQ', "https://faq.demarches-simplifiees.fr"
+ \-
+ = link_to 'Documentation', "http://doc.demarches-simplifiees.fr"
diff --git a/app/views/new_user/dossiers/purge_champ_piece_justificative.js.erb b/app/views/new_user/dossiers/purge_champ_piece_justificative.js.erb
index b564293b9..0aae810ba 100644
--- a/app/views/new_user/dossiers/purge_champ_piece_justificative.js.erb
+++ b/app/views/new_user/dossiers/purge_champ_piece_justificative.js.erb
@@ -1,2 +1,5 @@
<%= render_flash(timeout: 5000, sticky: true) %>
<%= remove_element("#piece_justificative_#{@champ.id}") %>
+
+let fileInputSelector = '<%= "#champs_#{@champ.id}" %>';
+document.querySelector(fileInputSelector).classList.remove('hidden');
diff --git a/app/views/root/patron.html.haml b/app/views/root/patron.html.haml
index 048804ceb..2988473e2 100644
--- a/app/views/root/patron.html.haml
+++ b/app/views/root/patron.html.haml
@@ -78,6 +78,11 @@
= link_to ".button.without-continuation", "#", class: "button without-continuation"
+ %p
+ = link_to ".button.small", "#", class: "button small"
+
+ = link_to ".button.small.primary", "#", class: "button small primary"
+
%p
= link_to ".button.large", "#", class: "button large"
diff --git a/app/views/shared/champs/piece_justificative/_pj_link.html.haml b/app/views/shared/champs/piece_justificative/_pj_link.html.haml
index d808058a4..5e7fd5dc8 100644
--- a/app/views/shared/champs/piece_justificative/_pj_link.html.haml
+++ b/app/views/shared/champs/piece_justificative/_pj_link.html.haml
@@ -1,7 +1,13 @@
- pj = champ.piece_justificative_file
-- if champ.virus_scan.present?
- - if champ.virus_scan.safe?
- = link_to pj.filename.to_s, url_for(pj), target: '_blank'
+
+.pj-link
+ - if champ.virus_scan.safe? || champ.virus_scan.blank?
+ = link_to url_for(pj), target: '_blank', title: "Télécharger la pièce jointe" do
+ %span.icon.attachment
+ = pj.filename.to_s
+ - if champ.virus_scan.blank?
+ (ce fichier n'a pas été analysé par notre antivirus, téléchargez-le avec précaution)
+
- else
= pj.filename.to_s
- if champ.virus_scan.pending?
@@ -13,6 +19,3 @@
(virus détecté, merci d'envoyer un autre fichier)
- else
(virus détecté, le téléchargement de ce fichier est bloqué)
-- else
- = link_to pj.filename.to_s, url_for(pj), target: '_blank'
- (ce fichier n'a pas été analysé par notre antivirus, téléchargez-le avec précaution)
diff --git a/app/views/shared/dossiers/editable_champs/_carte.html.haml b/app/views/shared/dossiers/editable_champs/_carte.html.haml
index eb218fa97..23c7c4e2e 100644
--- a/app/views/shared/dossiers/editable_champs/_carte.html.haml
+++ b/app/views/shared/dossiers/editable_champs/_carte.html.haml
@@ -8,4 +8,4 @@
= render partial: 'shared/champs/carte/geo_areas', locals: { champ: champ, error: false }
= form.hidden_field :value,
- data: { remote: true, url: champs_carte_path(form.index), params: { champ_id: champ&.id }.to_query, method: 'post' }
+ data: { remote: true, url: champs_carte_path(form.index), params: champ_carte_params(champ).to_query, method: 'post' }
diff --git a/app/views/shared/dossiers/editable_champs/_piece_justificative.html.haml b/app/views/shared/dossiers/editable_champs/_piece_justificative.html.haml
index 95df8fb56..da1785fb2 100644
--- a/app/views/shared/dossiers/editable_champs/_piece_justificative.html.haml
+++ b/app/views/shared/dossiers/editable_champs/_piece_justificative.html.haml
@@ -1,24 +1,24 @@
- pj = champ.piece_justificative_file
-- if champ.type_de_champ.piece_justificative_template.attached?
- %p.edit-pj-template.mb-1
- Veuillez télécharger, remplir et joindre
- = link_to('le modèle suivant', url_for(champ.type_de_champ.piece_justificative_template), target: '_blank')
+.piece-justificative
+ - if champ.type_de_champ.piece_justificative_template.attached?
+ %p.edit-pj-template.mb-1
+ Veuillez télécharger, remplir et joindre
+ = link_to('le modèle suivant', url_for(champ.type_de_champ.piece_justificative_template), target: '_blank')
+
+ - if pj.attached?
+ .piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
+ .piece-justificative-action
+ = render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
+ .piece-justificative-action
+ - if champ.private?
+ = link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
+ - else
+ = link_to 'Supprimer', champ_purge_champ_piece_justificative_path(id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
+ .piece-justificative-action
+ = button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#champs_#{champ.id}" }
-- if !pj.attached?
- = form.file_field :piece_justificative_file,
- id: "champs_#{champ.id}",
- direct_upload: true
-- else
- %div{ id: "piece_justificative_#{champ.id}" }
- = render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
- %br
- - if champ.private?
- = link_to 'supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete
- - else
- = link_to 'supprimer', champ_purge_champ_piece_justificative_path(id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete
- %br
- Modifier :
= form.file_field :piece_justificative_file,
id: "champs_#{champ.id}",
+ class: "piece-justificative-input #{'hidden' if pj.attached?}",
direct_upload: true
diff --git a/lib/tasks/support.rake b/lib/tasks/support.rake
index 3c00b017d..6bf178709 100644
--- a/lib/tasks/support.rake
+++ b/lib/tasks/support.rake
@@ -130,4 +130,28 @@ namespace :support do
user.update(email: new_email)
end
+
+ desc <<~EOD
+ Activate feature publish draft
+ EOD
+ task activate_publish_draft: :environment do
+ start_with = ENV['START_WITH']
+
+ administrateurs = Administrateur.where("email like ?", "#{start_with}%")
+
+ rake_puts("Activating publish draft for #{administrateurs.count} administrateurs...")
+
+ administrateurs.each do |a|
+ rake_puts("Activating publish draft for #{a.email}")
+ a.features["publish_draft"] = true
+ a.save
+
+ a.procedures.brouillon.each do |p|
+ if p.path.nil?
+ p.path = SecureRandom.uuid
+ p.save
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/new_administrateur/types_de_champ_spec.rb b/spec/features/new_administrateur/types_de_champ_spec.rb
index 1ad203998..801f3c070 100644
--- a/spec/features/new_administrateur/types_de_champ_spec.rb
+++ b/spec/features/new_administrateur/types_de_champ_spec.rb
@@ -117,4 +117,19 @@ feature 'As an administrateur I can edit types de champ', js: true do
expect(page).to have_content('Supprimer', count: 3)
end
+
+ it "Add carte champ" do
+ select('Carte', from: 'procedure_types_de_champ_attributes_0_type_champ')
+ fill_in 'procedure_types_de_champ_attributes_0_libelle', with: 'libellé de champ carte'
+ blur
+ check 'Quartiers prioritaires'
+ expect(page).to have_content('Formulaire enregistré')
+
+ preview_window = window_opened_by { click_on 'Prévisualiser le formulaire' }
+ within_window(preview_window) do
+ expect(page).to have_content('libellé de champ carte')
+ expect(page).to have_content('Quartiers prioritaires')
+ expect(page).not_to have_content('Cadastres')
+ end
+ end
end
diff --git a/spec/features/new_user/brouillon_spec.rb b/spec/features/new_user/brouillon_spec.rb
index 16c2d82ba..c0a495ade 100644
--- a/spec/features/new_user/brouillon_spec.rb
+++ b/spec/features/new_user/brouillon_spec.rb
@@ -1,4 +1,4 @@
-require 'spec_helper'
+require 'rails_helper'
feature 'The user' do
let(:password) { 'secret_password' }
@@ -10,7 +10,6 @@ feature 'The user' do
# TODO: check
# the order
# there are no extraneous input
- # attached file works
scenario 'fill a dossier', js: true do
allow(Champs::RegionChamp).to receive(:regions).and_return(['region1', 'region2']).at_least(:once)
allow(Champs::DepartementChamp).to receive(:departements).and_return(['dep1', 'dep2']).at_least(:once)
@@ -38,10 +37,10 @@ feature 'The user' do
select('dep2', from: 'departements')
check('engagement')
fill_in('dossier_link', with: '123')
- # do not know how to make it work...
- # find('form input[type="file"]').set(Rails.root.join('spec/fixtures/files/white.png'))
+ find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/file.pdf')
click_on 'Enregistrer le brouillon'
+ expect(page).to have_content('Votre brouillon a bien été sauvegardé')
# check data on the dossier
expect(user_dossier.brouillon?).to be true
@@ -62,8 +61,10 @@ feature 'The user' do
expect(champ_value_for('departements')).to eq('dep2')
expect(champ_value_for('engagement')).to eq('on')
expect(champ_value_for('dossier_link')).to eq('123')
+ expect(champ_value_for('piece_justificative')).to be_nil # antivirus hasn't approved the file yet
## check data on the gui
+
expect(page).to have_field('text', with: 'super texte')
expect(page).to have_field('textarea', with: 'super textarea')
expect(page).to have_field('date', with: '2012-12-12')
@@ -81,6 +82,8 @@ feature 'The user' do
expect(page).to have_select('departement', selected: 'dep2')
expect(page).to have_checked_field('engagement')
expect(page).to have_field('dossier_link', with: '123')
+ expect(page).to have_text('file.pdf')
+ expect(page).to have_text('analyse antivirus en cours')
end
let(:procedure_with_repetition) do
@@ -147,6 +150,48 @@ feature 'The user' do
expect(page).to have_current_path(merci_dossier_path(user_dossier))
end
+ let(:procedure_with_pj) do
+ tdcs = [create(:type_de_champ_piece_justificative, mandatory: true, libelle: 'Pièce justificative')]
+ create(:procedure, :published, :for_individual, types_de_champ: tdcs)
+ end
+
+ scenario 'adding, replacing and removing attachments', js: true do
+ log_in(user.email, password, procedure_with_pj)
+ fill_individual
+
+ # Add an attachment
+ find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/file.pdf')
+ click_on 'Enregistrer le brouillon'
+ expect(page).to have_content('Votre brouillon a bien été sauvegardé')
+ expect(page).to have_text('file.pdf')
+ expect(page).to have_text('analyse antivirus en cours')
+
+ # Mark file as scanned and clean
+ virus_scan = VirusScan.last
+ virus_scan.update(scanned_at: Time.zone.now, status: VirusScan.statuses.fetch(:safe))
+ within '.piece-justificative' do
+ click_on 'rafraichir'
+ end
+ expect(page).to have_link('file.pdf')
+ expect(page).to have_no_content('analyse antivirus en cours')
+
+ # Replace the attachment
+ within '.piece-justificative' do
+ click_on 'Remplacer'
+ end
+ find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/RIB.pdf')
+ click_on 'Enregistrer le brouillon'
+ expect(page).to have_no_text('file.pdf')
+ expect(page).to have_text('RIB.pdf')
+
+ # Remove the attachment
+ within '.piece-justificative' do
+ click_on 'Supprimer'
+ end
+ expect(page).to have_content('La pièce jointe a bien été supprimée')
+ expect(page).to have_no_text('RIB.pdf')
+ end
+
private
def log_in(email, password, procedure)
diff --git a/spec/lib/tasks/activate_publish_draft_spec.rb b/spec/lib/tasks/activate_publish_draft_spec.rb
new file mode 100644
index 000000000..567fdefed
--- /dev/null
+++ b/spec/lib/tasks/activate_publish_draft_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe 'activate_publish_draft#clean' do
+ let(:rake_task) { Rake::Task['support:activate_publish_draft'] }
+
+ let(:administrateur) { create(:administrateur) }
+ let!(:procedure) { create(:procedure, administrateur: administrateur) }
+ let!(:procedure2) { create(:simple_procedure, administrateur: administrateur) }
+
+ before do
+ ENV['START_WITH'] = administrateur.email
+ rake_task.invoke
+ administrateur.reload
+ end
+
+ after { rake_task.reenable }
+
+ it 'activate feature for administrateur' do
+ expect(administrateur.features["publish_draft"]).to eq(true)
+ end
+
+ it 'create a path for his brouillon procedure' do
+ expect(administrateur.procedures.brouillon.count).to eq(1)
+ expect(administrateur.procedures.brouillon.first.path).not_to eq(nil)
+ end
+
+ it 'does not change the path of his published procedure' do
+ expect(administrateur.procedures.publiee.count).to eq(1)
+ expect(administrateur.procedures.publiee.first.path).to eq(procedure2.path)
+ end
+end