Merge pull request #4280 from Keirua/procedure-new-design

Administrateur : nouvelle page de création et d'édition des démarche
This commit is contained in:
Pierre de La Morinerie 2019-10-31 10:42:45 +01:00 committed by GitHub
commit a74fa9b1bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 781 additions and 603 deletions

View file

Before

Width:  |  Height:  |  Size: 530 B

After

Width:  |  Height:  |  Size: 530 B

View file

@ -7,6 +7,7 @@ $default-padding: 2 * $default-spacer;
// layouts // layouts
$two-columns-padding: 60px; $two-columns-padding: 60px;
$two-columns-breakpoint: 980px; $two-columns-breakpoint: 980px;
$sub-header-bottom-margin: 3 * $default-spacer;
// z-order // z-order
$alert-z-index: 100; $alert-z-index: 100;

View file

@ -1,11 +1,11 @@
@import "../constants"; @import "constants";
.piece-justificative-actions { .attachment-actions {
display: flex; display: flex;
margin-bottom: $default-spacer; margin-bottom: $default-spacer;
} }
.piece-justificative-action { .attachment-action {
margin-right: $default-spacer; margin-right: $default-spacer;
.button { .button {
@ -13,6 +13,6 @@
} }
} }
.piece-justificative-input.hidden { .attachment-input.hidden {
display: none; display: none;
} }

View file

@ -61,11 +61,13 @@ sup {
.container { .container {
@include horizontal-padding($default-padding); @include horizontal-padding($default-padding);
max-width: $page-width + 2 * $default-padding; max-width: $page-width + 2 * $default-padding;
margin: 0 auto; margin-left: auto;
margin-right: auto;
} }
.small-container { .small-container {
@include horizontal-padding($default-padding); @include horizontal-padding($default-padding);
max-width: $small-page-width + 2 * $default-padding; max-width: $small-page-width + 2 * $default-padding;
margin: 0 auto; margin-left: auto;
margin-right: auto;
} }

View file

@ -70,12 +70,14 @@
// Align checkboxes on the top-left side of the label // Align checkboxes on the top-left side of the label
&.editable-champ-checkbox, &.editable-champ-checkbox,
&.editable-champ-radio.vertical,
&.editable-champ-engagement { &.editable-champ-engagement {
label { label {
padding-left: 28px; padding-left: 28px;
} }
input[type=checkbox] { input[type=checkbox],
input[type=radio] {
position: absolute; position: absolute;
top: 1px; top: 1px;
left: 0px; left: 0px;
@ -92,18 +94,6 @@
margin-left: 0; margin-left: 0;
} }
} }
&.vertical {
label {
display: block;
margin-left: 0;
margin-bottom: 0;
}
input[type=radio] {
margin-bottom: 16px;
}
}
} }
input[type=text]:not([data-address='true']), input[type=text]:not([data-address='true']),
@ -114,7 +104,7 @@
input[type=tel], input[type=tel],
textarea, textarea,
select, select,
.piece-justificative { .attachment {
display: block; display: block;
margin-bottom: 2 * $default-padding; margin-bottom: 2 * $default-padding;

View file

@ -59,8 +59,8 @@
background-image: image-url("icons/bubble.svg"); background-image: image-url("icons/bubble.svg");
} }
&.attachment { &.attached {
background-image: image-url("icons/attachment.svg"); background-image: image-url("icons/attached.svg");
} }
&.preview { &.preview {

View file

@ -52,3 +52,20 @@
.blank-tab { .blank-tab {
text-align: center; text-align: center;
} }
.sticky--top {
position: sticky;
// scss-lint:disable VendorPrefix
position: -webkit-sticky; // This is needed on Safari (tested on 12.1)
// scss-lint:enable VendorPrefix
top: 0;
}
.sticky--bottom {
position: sticky;
// scss-lint:disable VendorPrefix
position: -webkit-sticky; // This is needed on Safari (tested on 12.1)
// scss-lint:enable VendorPrefix
bottom: 0;
}

View file

@ -0,0 +1,79 @@
@import "colors";
@import "constants";
// scss-lint:disable SelectorFormat
.procedure-form .page-title {
text-align: left;
}
.procedure-form__columns {
display: flex;
margin-top: -$sub-header-bottom-margin;
}
// We want to make the form as large as possible,
// without shrinking the preview too much.
//
// A balanced compromised seems to make the form
// slighly larger than the preview (flex 10/9), and
// to reduce the margins on the preview as much as
// possible.
.procedure-form__column--form {
flex: 10;
padding: 0 $default-padding;
background-color: $light-grey;
}
.procedure-form__column--preview {
flex: 9;
padding: 0 2 * $default-padding;
// Gain a little horizontal space by using the white
// space on the right as our margin.
padding-right: 0;
}
// Hide the preview panel on smaller screens
@media screen and (max-width: 800px) {
.procedure-form__column--preview {
display: none;
}
}
.procedure-form__preview .procedure-preview {
max-width: 450px;
margin: 0 auto;
}
.procedure-form__preview-title {
font-size: 1.2rem;
font-weight: bold;
opacity: 0.5;
margin-top: $default-spacer * 4;
margin-bottom: $default-spacer * 8;
}
.procedure-form__actions {
display: flex;
margin-left: -$default-padding;
margin-right: -$default-padding;
padding: $default-spacer $default-padding;
background: $light-grey;
border-top: 1px solid $border-grey;
.button.send {
margin-left: auto;
}
}
.procedure-form__options-details {
margin-bottom: $default-padding;
}
.procedure-form__options-summary {
cursor: pointer;
.header-section {
display: inline-block;
}
}

View file

@ -4,7 +4,7 @@
.sub-header { .sub-header {
background-color: $light-grey; background-color: $light-grey;
padding-top: $default-padding; padding-top: $default-padding;
margin-bottom: 3 * $default-spacer; margin-bottom: $sub-header-bottom-margin;
border-bottom: 1px solid $border-grey; border-bottom: 1px solid $border-grey;
.container { .container {

View file

@ -2,7 +2,7 @@ class Admin::ProceduresController < AdminController
include SmartListing::Helper::ControllerExtensions include SmartListing::Helper::ControllerExtensions
helper SmartListing::Helper helper SmartListing::Helper
before_action :retrieve_procedure, only: [:show, :edit, :delete_logo, :delete_deliberation, :delete_notice, :monavis, :update_monavis, :publish_validate, :publish] before_action :retrieve_procedure, only: [:show, :delete_logo, :delete_deliberation, :delete_notice, :publish_validate, :publish]
def index def index
if current_administrateur.procedures.count != 0 if current_administrateur.procedures.count != 0
@ -49,9 +49,6 @@ class Admin::ProceduresController < AdminController
@current_administrateur = current_administrateur @current_administrateur = current_administrateur
end end
def edit
end
def destroy def destroy
procedure = current_administrateur.procedures.find(params[:id]) procedure = current_administrateur.procedures.find(params[:id])
@ -66,40 +63,6 @@ class Admin::ProceduresController < AdminController
redirect_to admin_procedures_draft_path redirect_to admin_procedures_draft_path
end end
def new
@procedure ||= Procedure.new(for_individual: true)
end
def create
@procedure = Procedure.new(procedure_params.merge(administrateurs: [current_administrateur]))
if !@procedure.save
flash.now.alert = @procedure.errors.full_messages
render 'new'
else
flash.notice = 'Démarche enregistrée.'
current_administrateur.instructeur.assign_to_procedure(@procedure)
redirect_to champs_procedure_path(@procedure)
end
end
def update
@procedure = current_administrateur.procedures.find(params[:id])
if !@procedure.update(procedure_params)
flash.now.alert = @procedure.errors.full_messages
render 'edit'
elsif @procedure.brouillon?
reset_procedure
flash.notice = 'Démarche modifiée. Tous les dossiers de cette démarche ont été supprimés.'
redirect_to edit_admin_procedure_path(id: @procedure.id)
else
flash.notice = 'Démarche modifiée.'
redirect_to edit_admin_procedure_path(id: @procedure.id)
end
end
def publish_validate def publish_validate
@procedure.assign_attributes(publish_params) @procedure.assign_attributes(publish_params)
end end
@ -182,18 +145,6 @@ class Admin::ProceduresController < AdminController
render layout: 'application' render layout: 'application'
end end
def monavis
end
def update_monavis
if !@procedure.update(procedure_params)
flash.now.alert = @procedure.errors.full_messages
else
flash.notice = 'le champ MonAvis a bien été mis à jour'
end
render 'monavis'
end
def active_class def active_class
@active_class = 'active' @active_class = 'active'
end end
@ -238,7 +189,7 @@ class Admin::ProceduresController < AdminController
end end
def procedure_params def procedure_params
editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :euro_flag, :logo, :auto_archive_on, :monavis_embed] editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :euro_flag, :logo, :auto_archive_on]
permited_params = if @procedure&.locked? permited_params = if @procedure&.locked?
params.require(:procedure).permit(*editable_params) params.require(:procedure).permit(*editable_params)
else else

View file

@ -9,7 +9,7 @@ module NewAdministrateur
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
flash.alert = 'Démarche inexistante' flash.alert = 'Démarche inexistante'
redirect_to admin_procedures_path redirect_to admin_procedures_path, status: 404
end end
def procedure_locked? def procedure_locked?

View file

@ -1,6 +1,6 @@
module NewAdministrateur module NewAdministrateur
class ProcedureAdministrateursController < AdministrateurController class ProcedureAdministrateursController < AdministrateurController
before_action :retrieve_procedure before_action :retrieve_procedure, except: [:new]
def index def index
end end

View file

@ -1,6 +1,6 @@
module NewAdministrateur module NewAdministrateur
class ProceduresController < AdministrateurController class ProceduresController < AdministrateurController
before_action :retrieve_procedure, only: [:champs, :annotations] before_action :retrieve_procedure, only: [:champs, :annotations, :edit, :monavis, :update_monavis]
before_action :procedure_locked?, only: [:champs, :annotations] before_action :procedure_locked?, only: [:champs, :annotations]
def apercu def apercu
@ -8,6 +8,55 @@ module NewAdministrateur
@tab = apercu_tab @tab = apercu_tab
end end
def new
@procedure ||= Procedure.new(for_individual: true)
end
def edit
end
def create
@procedure = Procedure.new(procedure_params.merge(administrateurs: [current_administrateur]))
if !@procedure.save
flash.now.alert = @procedure.errors.full_messages
render 'new'
else
flash.notice = 'Démarche enregistrée.'
current_administrateur.instructeur.assign_to_procedure(@procedure)
redirect_to champs_procedure_path(@procedure)
end
end
def update
@procedure = current_administrateur.procedures.find(params[:id])
if !@procedure.update(procedure_params)
flash.now.alert = @procedure.errors.full_messages
render 'edit'
elsif @procedure.brouillon?
reset_procedure
flash.notice = 'Démarche modifiée. Tous les dossiers de cette démarche ont été supprimés.'
redirect_to edit_admin_procedure_path(id: @procedure.id)
else
flash.notice = 'Démarche modifiée.'
redirect_to edit_admin_procedure_path(id: @procedure.id)
end
end
def monavis
end
def update_monavis
if !@procedure.update(procedure_params)
flash.now.alert = @procedure.errors.full_messages
else
flash.notice = 'le champ MonAvis a bien été mis à jour'
end
render 'monavis'
end
private private
def apercu_tab def apercu_tab
@ -17,5 +66,15 @@ module NewAdministrateur
def procedure_without_control def procedure_without_control
Procedure.find(params[:id]) Procedure.find(params[:id])
end end
def procedure_params
editable_params = [:libelle, :description, :organisation, :direction, :lien_site_web, :cadre_juridique, :deliberation, :notice, :web_hook_url, :euro_flag, :logo, :auto_archive_on, :monavis_embed]
permited_params = if @procedure&.locked?
params.require(:procedure).permit(*editable_params)
else
params.require(:procedure).permit(*editable_params, :duree_conservation_dossiers_dans_ds, :duree_conservation_dossiers_hors_ds, :for_individual, :path)
end
permited_params
end
end end
end end

View file

@ -0,0 +1,17 @@
import { delegate } from '@utils';
function syncInputToElement(fromSelector, toSelector) {
const fromElement = document.querySelector(fromSelector);
const toElement = document.querySelector(toSelector);
if (toElement && fromElement) {
toElement.innerText = fromElement.value;
}
}
function syncFormToPreview() {
syncInputToElement('#procedure_libelle', '.procedure-title');
syncInputToElement('#procedure_description', '.procedure-description-body');
}
delegate('input', '.procedure-form #procedure_libelle', syncFormToPreview);
delegate('input', '.procedure-form #procedure_description', syncFormToPreview);

View file

@ -22,6 +22,7 @@ import '../shared/toggle-target';
import '../new_design/dropdown'; import '../new_design/dropdown';
import '../new_design/form-validation'; import '../new_design/form-validation';
import '../new_design/procedure-context'; import '../new_design/procedure-context';
import '../new_design/procedure-form';
import '../new_design/select2'; import '../new_design/select2';
import '../new_design/spinner'; import '../new_design/spinner';
import '../new_design/support'; import '../new_design/support';

View file

@ -1,142 +0,0 @@
- if @procedure.locked?
.alert.alert-info
Cette démarche est publiée, certains éléments de la description ne sont plus modifiables
.form-group
%h4 Libellé*
= f.text_field :libelle, class: 'form-control', placeholder: 'Libellé de la démarche'
.form-group
%h4 Description*
= f.text_area :description, rows: '6', placeholder: 'Description du projet', class: 'form-control'
- if !@procedure.locked?
.form-group
%h4 Conservation des données
= f.label :duree_conservation_dossiers_dans_ds, "Sur demarches-simplifiees.fr* (durée en mois après le début de linstruction)"
= f.number_field :duree_conservation_dossiers_dans_ds, class: 'form-control', placeholder: '6', required: true
= f.label :duree_conservation_dossiers_hors_ds, "Hors demarches-simplifiees.fr* (durée en mois après la fin de l'instruction)"
= f.number_field :duree_conservation_dossiers_hors_ds, class: 'form-control', placeholder: '6', required: true
- if @procedure.created_at.present?
.form-group
%h4 Où les usagers trouveront-ils le lien vers la démarche ?
= f.text_field :lien_site_web, class: 'form-control', placeholder: 'https://exemple.gouv.fr/ma_demarche'
.form-group
%h4 Cadre juridique *
%p Texte qui justifie le droit de collecter les données demandées dans votre démarche auprès des usagers, par exemple :
%ul
%li Texte de loi (loi, décret, circulaire, arrêté,…)
%li Texte juridique (statuts, délibération, décision du conseil d'administration…)
%li
= link_to("En savoir plus avec cette vidéo de 5 minutes", CADRE_JURIDIQUE_URL, target: "_blank", rel: "noopener")
%p.help-block
%i.fa.fa-info-circle
Vous pouvez saisir un lien vers ce texte ou importer celui-ci directement.
.row
.col-md-6
= f.label :cadre_juridique, 'Lien vers le texte'
= f.text_field :cadre_juridique, class: 'form-control', placeholder: 'https://www.legifrance.gouv.fr/'
.col-md-6
= f.label :deliberation, 'Importer le texte'
- deliberation = @procedure.deliberation
- if !deliberation.attached?
= f.file_field :deliberation,
direct_upload: true
- else
%a{ href: url_for(deliberation), target: '_blank', rel: 'noopener' }
= deliberation.filename.to_s
- if @procedure.persisted?
= link_to 'supprimer', delete_deliberation_admin_procedure_path(@procedure), method: :delete
%br
Modifier :
= f.file_field :deliberation,
direct_upload: true
.form-group
%h4 Notice explicative de la démarche
- notice = @procedure.notice
- if !notice.attached?
= f.file_field :notice,
direct_upload: true
- else
%a{ href: url_for(notice), target: '_blank', rel: 'noopener' }
= notice.filename.to_s
- if @procedure.persisted?
\-
= link_to 'supprimer', delete_notice_admin_procedure_path(@procedure), method: :delete
%br
Modifier :
= f.file_field :notice,
direct_upload: true
.row
.col-md-6
%h4 Logo de la démarche
- if @procedure.logo.attached?
= image_tag @procedure.logo_url, { style: 'height: 40px; display: inline; margin-right: 6px;', id: 'preview_procedure_logo' }
\-
- if @procedure.persisted?
= link_to 'supprimer', delete_logo_admin_procedure_path(@procedure), method: :delete
= f.file_field :logo,
direct_upload: true,
accept: 'image/png, image/jpg, image/jpeg',
style: 'display: inline'
%div{ style: 'margin-top: 5px;' }
%i
Fichier accepté : JPG / JPEG / PNG
.col-md-6
%h4 Drapeau européen
.checkbox
%label
= f.check_box :euro_flag
Afficher le drapeau européen
- if !@procedure.locked?
.row
.col-md-6
%h4 À qui sadresse ma démarche ?
.checkbox
%label
= f.radio_button :for_individual, true
%b Ma démarche sadresse à un particulier
%p
En choisissant cette option, lusager devra renseigner son nom et prénom avant daccéder au formulaire
.checkbox
%label
= f.radio_button :for_individual, false
%b Ma démarche sadresse à une personne morale
%p
En choisissant cette option, lusager devra renseigner son n° SIRET. Grâce à lAPI Entreprise, seront alors automatiquement remontées les informations sur la personne morale type raison sociale ou adresse du siège social.
%b
Si votre démarche sadresse indifféremment à une personne morale ou un particulier choisissez l'option "particuliers". Vous pourrez utilisez le champ SIRET directement dans le formulaire.
.row
.col-md-6
%h4 Options avancées
- if feature_enabled?(:administrateur_web_hook)
%label{ for: :web_hook_url } Lien de rappel HTTP (webhook)
= f.text_field :web_hook_url, class: 'form-control', placeholder: 'https://callback.exemple.fr/'
%p.help-block
%i.fa.fa-info-circle
Vous pouvez définir un lien de rappel HTTP (aussi appelé webhook) pour notifier un service tiers du changement de l'état dun dossier de cette démarche sur demarches-simplifiees.fr.
= link_to("Consulter la documentation du webhook", WEBHOOK_DOC_URL, target: "_blank", rel: "noopener")
\.
%label{ for: :auto_archive_on } Clôture automatique le
= f.date_field :auto_archive_on, id: 'auto_archive_on', value: @procedure.auto_archive_on
(à 00h01)
%p.help-block
%i.fa.fa-info-circle
La clôture automatique suspend la publication de la démarche et entraîne le passage de tous les dossiers au statut "en instruction", ce qui ne permet plus aux usagers de les modifier. Le passage en instruction des dossiers s'accompagne de l'envoi de l'email d'accusé de passage en instruction (configurable par l'administrateur dans la partie "E-mail" de la démarche).

View file

@ -1,17 +0,0 @@
.form-group
%h3 Insérer un lien vers « MonAvis »
%p
Proposez aux usagers de donner un avis sur votre démarche. Pour ce faire, vous devez précédemment aller sur «
%a{ :href => "https://monavis.numerique.gouv.fr" } https://monavis.numerique.gouv.fr
», créer un compte, et référencer là démarche que vous venez de publier.
%p
Vous pouvez
%a{ :href => "https://doc.demarches-simplifiees.fr/tutoriels/integration-du-bouton-mon-avis" } consulter notre tutoriel complet
pour intégrer le bouton « MonAvis » sur demarches-simplifiees.fr.
%p Une fois en possession du code généré sur le site MonAvis, vous pouvez le coller dans le champ ci-dessous :
.form-group
= f.label :monavis_embed, "Mon avis"
= f.text_area :monavis_embed, rows: '6', placeholder: '<a href="https://monavis.numerique.gouv.fr/Demarches/123456?&view-mode=formulaire-avis&nd_mode=en-ligne-enti%C3%A8rement&nd_source=button&key=cd4a872d4"><img src="https://monavis.numerique.gouv.fr/monavis-static/bouton-bleu.png" alt="Je donne mon avis" title="Je donne mon avis sur cette démarche" /></a>', class: 'form-control'

View file

@ -1,6 +0,0 @@
.row.white-back
#procedure_new.section.section-label
= form_for @procedure, url: url_for({ controller: 'admin/procedures', action: :update, id: @procedure.id }), multipart: true do |f|
= render partial: 'informations', locals: { f: f }
.text-right
= f.button 'Enregistrer', class: 'btn btn-success'

View file

@ -1,6 +0,0 @@
.row.white-back
#procedure_new.section.section-label
= form_for @procedure, url: url_for({ controller: 'admin/procedures', action: :update_monavis }), multipart: true do |f|
= render partial: 'monavis', locals: { f: f }
.text-right
= f.button 'Enregistrer', class: 'btn btn-success'

View file

@ -1,9 +0,0 @@
.row.white-back
%h2
= t('dynamics.admin.dossiers.tableau_de_bord.nouvelle_procedure')
#procedure_new.section.section-label
= form_for @procedure, url: { controller: 'admin/procedures', action: :create }, multipart: true do |f|
= render partial: 'informations', locals: { f: f }
.text-center
= f.button 'Valider', class: 'btn btn-info btn-lg btn-block', id: 'save-procedure'

View file

@ -1,3 +1,3 @@
<%= render_flash(timeout: 5000, sticky: true) %> <%= render_flash(timeout: 5000, sticky: true) %>
<%= remove_element("#piece_justificative_#{@attachment_id}") %> <%= remove_element("#attachment_#{@attachment_id}") %>
<%= show_element("#piece_justificative_file_#{@attachment_id}") %> <%= show_element("#attachment_file_#{@attachment_id}") %>

View file

@ -1,4 +1,4 @@
<%= render_to_element(".pj-link[data-attachment-id=\"#{@attachment.id}\"]", <%= render_to_element(".attachment-link[data-attachment-id=\"#{@attachment.id}\"]",
partial: 'shared/attachment/show', partial: 'shared/attachment/show',
outer: true, outer: true,
locals: { attachment: @attachment, user_can_upload: @user_can_upload }) %> locals: { attachment: @attachment, user_can_upload: @user_can_upload }) %>

View file

@ -13,7 +13,10 @@
= form_for @avis, url: instructeur_avis_path(@avis), html: { class: 'form' } do |f| = form_for @avis, url: instructeur_avis_path(@avis), html: { class: 'form' } do |f|
= f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true = f.text_area :answer, rows: 3, placeholder: 'Votre avis', required: true
= render partial: "shared/attachment/update", locals: { attachment: @avis.piece_justificative_file.attachment, user_can_destroy: true, form: f } = render 'shared/attachment/edit',
{ form: f,
attached_file: @avis.piece_justificative_file,
user_can_destroy: true }
.flex.justify-between.align-baseline .flex.justify-between.align-baseline
%p.confidentiel.flex %p.confidentiel.flex

View file

@ -10,7 +10,7 @@
- if !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty? - if !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty?
%span.dropdown.print-menu-opener %span.dropdown.print-menu-opener
%button.button.dropdown-button.icon-only %button.button.dropdown-button.icon-only
%span.icon.attachment %span.icon.attached
%ul.print-menu.dropdown-content %ul.print-menu.dropdown-content
%li %li
- if PiecesJustificativesService.pieces_justificatives_total_size(dossier) < Dossier::TAILLE_MAX_ZIP - if PiecesJustificativesService.pieces_justificatives_total_size(dossier) < Dossier::TAILLE_MAX_ZIP

View file

@ -6,7 +6,7 @@
.column.procedure-preview .column.procedure-preview
- if procedure - if procedure
= render partial: 'layouts/commencer/procedure_description', locals: { procedure: procedure } = render partial: 'shared/procedure_description', locals: { procedure: procedure }
- else - else
= render partial: 'layouts/commencer/no_procedure' = render partial: 'layouts/commencer/no_procedure'

View file

@ -0,0 +1,119 @@
- if @procedure.locked?
.card.warning
.card-title Cette démarche est publiée.
Certains éléments de la description ne sont plus modifiables.
= f.label :libelle do
Titre de la démarche
%span.mandatory *
= f.text_field :libelle, class: 'form-control', placeholder: 'Titre'
= f.label :description do
Description
%span.mandatory *
= f.text_area :description, rows: '6', placeholder: 'Description de la démarche, destinataires, etc. ', class: 'form-control'
%h2.header-section Logo de la démarche
= render 'shared/attachment/edit',
{ form: f,
attached_file: @procedure.logo,
accept: 'image/png, image/jpg, image/jpeg',
user_can_destroy: true }
- if !@procedure.locked?
%h2.header-section Conservation des données
= f.label :duree_conservation_dossiers_dans_ds do
Sur demarches-simplifiees.fr
%span.mandatory *
.notice (durée en mois après le début de linstruction)
= f.number_field :duree_conservation_dossiers_dans_ds, class: 'form-control', placeholder: '6', required: true
= f.label :duree_conservation_dossiers_hors_ds do
Hors demarches-simplifiees.fr
%span.mandatory *
.notice (durée en mois après la fin de l'instruction)
= f.number_field :duree_conservation_dossiers_hors_ds, class: 'form-control', placeholder: '6', required: true
- if @procedure.created_at.present?
= f.label :lien_site_web do
Où les usagers trouveront-ils le lien vers la démarche ?
= f.text_field :lien_site_web, class: 'form-control', placeholder: 'https://exemple.gouv.fr/ma_demarche'
%h2.header-section
Cadre juridique
%span.mandatory *
= f.label :cadre_juridique do
.notice
%p
Le cadre juridique justifie le droit de collecter les données demandées dans votre démarche auprès des usagers. Par exemple :
%br
 Texte de loi (loi, décret, circulaire, arrêté…)
%br
 Texte juridique (statuts, délibération, décision du conseil d'administration…)
%br
= link_to("En savoir plus avec cette vidéo de 5 minutes", CADRE_JURIDIQUE_URL, target: "_blank", rel: "noopener")
%p
Vous pouvez saisir un lien web vers ce texte, ou limporter depuis un fichier.
Lien vers le texte
= f.text_field :cadre_juridique, class: 'form-control', placeholder: 'https://www.legifrance.gouv.fr/'
= f.label :deliberation, 'Importer le texte'
= render 'shared/attachment/edit',
{ form: f,
attached_file: @procedure.deliberation,
user_can_destroy: true }
%h2.header-section
Notice explicative de la démarche
- notice = @procedure.notice
= f.label :notice do
.notice
%p
Une notice explicative est un document destiné à guider lusager dans sa démarche. Cest un document que vous avez élaboré et qui peut prendre la forme dun fichier doc, dun pdf ou encore de diapositives. Le bouton pour télécharger cette notice apparaît en haut du formulaire pour lusager.
= render 'shared/attachment/edit',
{ form: f,
attached_file: @procedure.notice,
user_can_destroy: true }
- if !@procedure.locked?
%h2.header-section À qui sadresse ma démarche ?
.editable-champ.editable-champ-radio.vertical
= f.label :for_individual, value: true do
Ma démarche sadresse à un particulier
%span.notice
%p En choisissant cette option, lusager devra renseigner son nom et prénom avant daccéder au formulaire
= f.radio_button :for_individual, true
.editable-champ.editable-champ-radio.vertical
= f.label :for_individual, value: false do
Ma démarche sadresse à une personne morale
%span.notice
%p En choisissant cette option, lusager devra renseigner son n° SIRET.<br>Grâce à lAPI Entreprise, les informations sur la personne morale (raison sociale, adresse du siège, etc.) seront automatiquement renseignées.
= f.radio_button :for_individual, false
%p.explication
Si votre démarche sadresse indifféremment à une personne morale ou un particulier, choisissez l'option « Particuliers ». Vous pourrez ajouter un champ SIRET directement dans le formulaire.
%details.procedure-form__options-details
%summary.procedure-form__options-summary
%h2.header-section Options avancées
- if feature_enabled?(:administrateur_web_hook)
= f.label :web_hook_url do
Lien de rappel HTTP (webhook)
= f.text_field :web_hook_url, class: 'form-control', placeholder: 'https://callback.exemple.fr/'
%p.help-block
%i.fa.fa-info-circle
Vous pouvez définir un lien de rappel HTTP (aussi appelé webhook) pour notifier un service tiers du changement de l'état dun dossier de cette démarche sur demarches-simplifiees.fr.
= link_to("Consulter la documentation du webhook", WEBHOOK_DOC_URL, target: "_blank", rel: "noopener")
\.
= f.label :auto_archive_on do
Clôture automatique à 00h01 le :
= f.date_field :auto_archive_on, id: 'auto_archive_on', value: @procedure.auto_archive_on
%p.explication
La clôture automatique suspend la publication de la démarche et entraîne le passage de tous les dossiers "en construction"
(c'est à dire ceux qui ont été déposés), au statut "en instruction", ce qui ne permet plus aux usagers de les modifier.

View file

@ -0,0 +1,15 @@
%p.explication
Proposez aux usagers de donner un avis sur votre démarche. Pour ce faire, vous devez précédemment aller sur «
%a{ :href => "https://monavis.numerique.gouv.fr" } https://monavis.numerique.gouv.fr
», créer un compte, et référencer là démarche que vous venez de publier.
%br
%br
Vous pouvez
%a{ :href => "https://doc.demarches-simplifiees.fr/tutoriels/integration-du-bouton-mon-avis" } consulter notre tutoriel complet
pour intégrer le bouton « MonAvis » sur demarches-simplifiees.fr.
%br
%br
Une fois en possession du code généré sur le site MonAvis, vous pouvez le coller dans le champ ci-dessous :
= f.label :monavis_embed, "Mon avis"
= f.text_area :monavis_embed, rows: '6', placeholder: '<a href="https://monavis.numerique.gouv.fr/Demarches/123456?&view-mode=formulaire-avis&nd_mode=en-ligne-enti%C3%A8rement&nd_source=button&key=cd4a872d4"><img src="https://monavis.numerique.gouv.fr/monavis-static/bouton-bleu.png" alt="Je donne mon avis" title="Je donne mon avis sur cette démarche" /></a>', class: 'form-control'

View file

@ -0,0 +1,24 @@
= render partial: 'new_administrateur/breadcrumbs',
locals: { steps: [link_to('Démarches', admin_procedures_path),
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
'Description'] }
.procedure-form
.procedure-form__columns.container
= form_for @procedure,
url: url_for({ controller: 'new_administrateur/procedures', action: :update, id: @procedure.id }),
multipart: true,
html: { class: 'form procedure-form__column--form' } do |f|
%h1.page-title Description
= render partial: 'new_administrateur/procedures/informations', locals: { f: f }
.procedure-form__actions.sticky--bottom
= link_to 'Annuler', admin_procedure_path(id: @procedure), class: 'button', data: { confirm: 'Êtes-vous sûr de vouloir annuler les modifications effectuées ?'}
= f.button 'Enregistrer', class: 'button primary send'
.procedure-form__column--preview
.procedure-form__preview.sticky--top
%h3.procedure-form__preview-title Aperçu
.procedure-preview
= render partial: 'shared/procedure_description', locals: { procedure: @procedure }

View file

@ -0,0 +1,15 @@
= render partial: 'new_administrateur/breadcrumbs',
locals: { steps: [link_to('Démarches', admin_procedures_path),
link_to(@procedure.libelle, admin_procedure_path(@procedure)),
link_to('MonAvis', admin_procedures_path)] }
.container
%h1.page-title
Insérer un lien vers « MonAvis »
.container
%h1
= form_for @procedure, url: url_for({ controller: 'new_administrateur/procedures', action: :update_monavis }), multipart: true, html: { class: 'form' } do |f|
= render partial: 'monavis', locals: { f: f }
.text-right
= f.button 'Enregistrer', class: 'button primary send'

View file

@ -0,0 +1,24 @@
= render partial: 'new_administrateur/breadcrumbs',
locals: { steps: [link_to('Démarches', admin_procedures_path),
'Nouvelle'] }
.procedure-form
.procedure-form__columns.container
= form_for @procedure,
url: url_for({ controller: 'new_administrateur/procedures', action: :create, id: @procedure.id }),
multipart: true,
html: { class: 'form procedure-form__column--form' } do |f|
%h1.page-title Nouvelle démarche
= render partial: 'new_administrateur/procedures/informations', locals: { f: f }
.procedure-form__actions.sticky--bottom
= link_to 'Annuler', admin_procedures_path, class: 'button', data: { confirm: 'Êtes-vous sûr de vouloir annuler la création de cette démarche ?'}
= f.button 'Créer la démarche', class: 'button primary send'
.procedure-form__column--preview
.procedure-form__preview.sticky--top
%h3.procedure-form__preview-title Aperçu
.procedure-preview
= render partial: 'shared/procedure_description', locals: { procedure: @procedure }

View file

@ -14,7 +14,7 @@
%span.icon.edit %span.icon.edit
%span.icon.in-progress %span.icon.in-progress
%span.icon.bubble %span.icon.bubble
%span.icon.attachment %span.icon.attached
%span.icon.lock %span.icon.lock
%span.icon.add %span.icon.add
%span.icon.justificatif %span.icon.justificatif
@ -48,9 +48,24 @@
= render partial: "shared/dossiers/editable_champs/editable_champ", = render partial: "shared/dossiers/editable_champs/editable_champ",
locals: { champ: champ, form: champ_form, seen_at: nil } locals: { champ: champ, form: champ_form, seen_at: nil }
.editable-champ.editable-champ-text
%label Mot de passe %label Mot de passe
%input{ type: "password", value: "12345678" } %input{ type: "password", value: "12345678" }
%h2.header-section Bouton radio verticaux
.editable-champ.editable-champ-radio.vertical
= f.label :archived, value: true do
Option A
%span.notice
%p Une option tout à fait valable.
= f.radio_button :archived, true
.editable-champ.editable-champ-radio.vertical
= f.label :archived, value: false do
Option B
%span.notice
%p Une autre option, pas mal non plus.
= f.radio_button :archived, false
.send-wrapper .send-wrapper
= f.submit 'Enregistrer un brouillon (formnovalidate)', formnovalidate: true, class: 'button send' = f.submit 'Enregistrer un brouillon (formnovalidate)', formnovalidate: true, class: 'button send'
= f.submit 'Envoyer', class: 'button send primary' = f.submit 'Envoyer', class: 'button send primary'

View file

@ -1,7 +1,7 @@
.procedure-logos .procedure-logos
= image_tag procedure.logo_url = image_tag procedure.logo_url
- if procedure.euro_flag - if procedure.euro_flag
= image_tag "flag_of_europe.svg" = image_tag("flag_of_europe.svg", id: 'euro_flag', class: (!procedure.euro_flag ? "hidden" : ""))
%h2.procedure-title %h2.procedure-title
= procedure.libelle = procedure.libelle
.procedure-description .procedure-description

View file

@ -0,0 +1,29 @@
-# Display a widget for uploading, editing and deleting a file attachment
- attachment = attached_file.attachment
- attachment_id = attachment ? attachment.id : SecureRandom.uuid
- persisted = attachment && attachment.persisted?
- accept = defined?(accept) ? accept : nil
- user_can_destroy = defined?(user_can_destroy) ? user_can_destroy : false
.attachment
- if defined?(template) && template.attached?
%p.mb-1
Veuillez télécharger, remplir et joindre
= link_to('le modèle suivant', url_for(template), target: '_blank', rel: 'noopener')
- if persisted
.attachment-actions{ id: "attachment_#{attachment_id}" }
.attachment-action
= render partial: "shared/attachment/show", locals: { attachment: attachment, user_can_upload: true }
- if user_can_destroy
.attachment-action
= link_to 'Supprimer', attachment_url(attachment.id, { signed_id: attachment.blob.signed_id }), remote: true, method: :delete, class: 'button small danger'
.attachment-action
= button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#attachment_file_#{attachment_id}" }
= form.file_field attached_file.name,
id: "attachment_file_#{attachment_id}",
class: "attachment-input #{'hidden' if persisted}",
accept: accept,
direct_upload: true

View file

@ -5,10 +5,10 @@
- else - else
- attachment_check_url = attachment_url(attachment.id, { signed_id: attachment.blob.signed_id, user_can_upload: user_can_upload }) - attachment_check_url = attachment_url(attachment.id, { signed_id: attachment.blob.signed_id, user_can_upload: user_can_upload })
.pj-link{ 'data-attachment-id': attachment.id, 'data-attachment-check-url': attachment_check_url } .attachment-link{ 'data-attachment-id': attachment.id, 'data-attachment-check-url': attachment_check_url }
- if should_display_link - if should_display_link
= link_to url_for(attachment.blob), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do = link_to url_for(attachment.blob), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
%span.icon.attachment %span.icon.attached
= attachment.filename.to_s = attachment.filename.to_s
- if !attachment.virus_scanner.started? - if !attachment.virus_scanner.started?
(ce fichier na pas été analysé par notre antivirus, téléchargez-le avec précaution) (ce fichier na pas été analysé par notre antivirus, téléchargez-le avec précaution)

View file

@ -1,24 +0,0 @@
.piece-justificative
- if defined?(template) && template.attached?
%p.edit-pj-template.mb-1
Veuillez télécharger, remplir et joindre
= link_to('le modèle suivant', url_for(template), target: '_blank', rel: 'noopener')
- attachment_id = attachment ? attachment.id : SecureRandom.uuid
- persisted = attachment && attachment.persisted?
- user_can_destroy = defined?(user_can_destroy) ? user_can_destroy : false
- if persisted
.piece-justificative-actions{ id: "piece_justificative_#{attachment_id}" }
.piece-justificative-action
= render partial: "shared/attachment/show", locals: { attachment: attachment, user_can_upload: true }
- if user_can_destroy
.piece-justificative-action
= link_to 'Supprimer', attachment_url(attachment.id, { signed_id: attachment.blob.signed_id }), remote: true, method: :delete, class: 'button small danger'
.piece-justificative-action
= button_tag 'Remplacer', type: 'button', class: 'button small', data: { 'toggle-target': "#piece_justificative_file_#{attachment_id}" }
= form.file_field :piece_justificative_file,
id: "piece_justificative_file_#{attachment_id}",
class: "piece-justificative-input #{'hidden' if persisted}",
direct_upload: true

View file

@ -1 +1,4 @@
= render partial: "shared/attachment/update", locals: { attachment: champ.piece_justificative_file.attachment, template: champ.type_de_champ.piece_justificative_template, user_can_destroy: true, form: form } = render 'shared/attachment/edit',
{ form: form,
attached_file: champ.piece_justificative_file,
template: champ.type_de_champ.piece_justificative_template, user_can_destroy: true }

View file

@ -154,13 +154,20 @@ Rails.application.routes.draw do
patch 'activate' => '/users/activate#create' patch 'activate' => '/users/activate#create'
end end
# order matters: we don't want those routes to match /admin/procedures/:id
get 'admin/procedures/new' => 'new_administrateur/procedures#new', as: :new_admin_procedure
get 'admin/procedures/:id/edit' => 'new_administrateur/procedures#edit', as: :edit_admin_procedure
post 'admin/procedures' => 'new_administrateur/procedures#create'
get 'admin/procedures/:id/monavis' => 'new_administrateur/procedures#monavis', as: :admin_procedure_monavis
patch 'admin/procedures/:id/monavis' => 'new_administrateur/procedures#update_monavis', as: :update_monavis
namespace :admin do namespace :admin do
get 'activate' => '/administrateurs/activate#new' get 'activate' => '/administrateurs/activate#new'
patch 'activate' => '/administrateurs/activate#create' patch 'activate' => '/administrateurs/activate#create'
get 'procedures/archived' => 'procedures#archived' get 'procedures/archived' => 'procedures#archived'
get 'procedures/draft' => 'procedures#draft' get 'procedures/draft' => 'procedures#draft'
resources :procedures do resources :procedures, except: [:new, :edit, :update] do
collection do collection do
get 'new_from_existing' => 'procedures#new_from_existing', as: :new_from_existing get 'new_from_existing' => 'procedures#new_from_existing', as: :new_from_existing
end end
@ -178,8 +185,6 @@ Rails.application.routes.draw do
put 'publish' => 'procedures#publish', as: :publish put 'publish' => 'procedures#publish', as: :publish
post 'transfer' => 'procedures#transfer', as: :transfer post 'transfer' => 'procedures#transfer', as: :transfer
put 'clone' => 'procedures#clone', as: :clone put 'clone' => 'procedures#clone', as: :clone
get 'monavis' => 'procedures#monavis', as: :monavis
patch 'monavis' => 'procedures#update_monavis', as: :update_monavis
resource :assigns, only: [:show, :update], path: 'instructeurs' resource :assigns, only: [:show, :update], path: 'instructeurs'
@ -350,7 +355,7 @@ Rails.application.routes.draw do
# #
scope module: 'new_administrateur' do scope module: 'new_administrateur' do
resources :procedures, only: [:update] do resources :procedures, only: [:update, :new] do
member do member do
get 'apercu' get 'apercu'
get 'champs' get 'champs'

View file

@ -146,192 +146,6 @@ describe Admin::ProceduresController, type: :controller do
end end
end end
describe 'GET #edit' do
let(:published_at) { nil }
let(:procedure) { create(:procedure, administrateur: admin, published_at: published_at) }
let(:procedure_id) { procedure.id }
subject { get :edit, params: { id: procedure_id } }
context 'when user is not connected' do
before do
sign_out(admin.user)
end
it { is_expected.to redirect_to new_user_session_path }
end
context 'when user is connected' do
context 'when procedure exist' do
let(:procedure_id) { procedure.id }
it { is_expected.to have_http_status(:success) }
end
context 'when procedure is published' do
let(:published_at) { Time.zone.now }
it { is_expected.to have_http_status(:success) }
end
context "when procedure doesn't exist" do
let(:procedure_id) { bad_procedure_id }
it { is_expected.to have_http_status(404) }
end
end
end
describe 'POST #create' do
context 'when all attributs are filled' do
describe 'new procedure in database' do
subject { post :create, params: { procedure: procedure_params } }
it { expect { subject }.to change { Procedure.count }.by(1) }
end
context 'when procedure is correctly save' do
before do
post :create, params: { procedure: procedure_params }
end
describe 'procedure attributs in database' do
subject { Procedure.last }
it { expect(subject.libelle).to eq(libelle) }
it { expect(subject.description).to eq(description) }
it { expect(subject.organisation).to eq(organisation) }
it { expect(subject.direction).to eq(direction) }
it { expect(subject.administrateurs).to eq([admin]) }
it { expect(subject.duree_conservation_dossiers_dans_ds).to eq(duree_conservation_dossiers_dans_ds) }
it { expect(subject.duree_conservation_dossiers_hors_ds).to eq(duree_conservation_dossiers_hors_ds) }
end
it { is_expected.to redirect_to(champs_procedure_path(Procedure.last)) }
it { expect(flash[:notice]).to be_present }
end
context 'when procedure is correctly saved' do
let(:instructeur) { admin.instructeur }
before do
post :create, params: { procedure: procedure_params }
end
describe "admin can also instruct the procedure as a instructeur" do
subject { Procedure.last }
it { expect(subject.defaut_groupe_instructeur.instructeurs).to include(instructeur) }
end
end
end
context 'when many attributs are not valid' do
let(:libelle) { '' }
let(:description) { '' }
describe 'no new procedure in database' do
subject { post :create, params: { procedure: procedure_params } }
it { expect { subject }.to change { Procedure.count }.by(0) }
describe 'no new module api carto in database' do
it { expect { subject }.to change { ModuleAPICarto.count }.by(0) }
end
end
describe 'flash message is present' do
before do
post :create, params: { procedure: procedure_params }
end
it { expect(flash[:alert]).to be_present }
end
end
end
describe 'PUT #update' do
let!(:procedure) { create(:procedure, :with_type_de_champ, administrateur: admin) }
context 'when administrateur is not connected' do
before do
sign_out(admin.user)
end
subject { put :update, params: { id: procedure.id } }
it { is_expected.to redirect_to new_user_session_path }
end
context 'when administrateur is connected' do
def update_procedure
put :update, params: { id: procedure.id, procedure: procedure_params }
procedure.reload
end
context 'when all attributs are present' do
let(:libelle) { 'Blable' }
let(:description) { 'blabla' }
let(:organisation) { 'plop' }
let(:direction) { 'plap' }
let(:duree_conservation_dossiers_dans_ds) { 7 }
let(:duree_conservation_dossiers_hors_ds) { 5 }
before { update_procedure }
describe 'procedure attributs in database' do
subject { procedure }
it { expect(subject.libelle).to eq(libelle) }
it { expect(subject.description).to eq(description) }
it { expect(subject.organisation).to eq(organisation) }
it { expect(subject.direction).to eq(direction) }
it { expect(subject.duree_conservation_dossiers_dans_ds).to eq(duree_conservation_dossiers_dans_ds) }
it { expect(subject.duree_conservation_dossiers_hors_ds).to eq(duree_conservation_dossiers_hors_ds) }
end
it { is_expected.to redirect_to(edit_admin_procedure_path id: procedure.id) }
it { expect(flash[:notice]).to be_present }
end
context 'when many attributs are not valid' do
before { update_procedure }
let(:libelle) { '' }
let(:description) { '' }
describe 'flash message is present' do
it { expect(flash[:alert]).to be_present }
end
end
context 'when procedure is brouillon' do
let(:procedure) { create(:procedure_with_dossiers, :with_path, :with_type_de_champ, administrateur: admin) }
let!(:dossiers_count) { procedure.dossiers.count }
describe 'dossiers are dropped' do
subject { update_procedure }
it {
expect(dossiers_count).to eq(1)
expect(subject.dossiers.count).to eq(0)
}
end
end
context 'when procedure is published' do
let(:procedure) { create(:procedure, :with_type_de_champ, :published, administrateur: admin) }
subject { update_procedure }
describe 'only some properties can be updated' do
it { expect(subject.libelle).to eq procedure_params[:libelle] }
it { expect(subject.description).to eq procedure_params[:description] }
it { expect(subject.organisation).to eq procedure_params[:organisation] }
it { expect(subject.direction).to eq procedure_params[:direction] }
it { expect(subject.for_individual).not_to eq procedure_params[:for_individual] }
end
end
end
end
describe 'PUT #publish' do describe 'PUT #publish' do
let(:procedure) { create(:procedure, administrateur: admin, lien_site_web: lien_site_web) } let(:procedure) { create(:procedure, administrateur: admin, lien_site_web: lien_site_web) }
let(:procedure2) { create(:procedure, :published, administrateur: admin, lien_site_web: lien_site_web) } let(:procedure2) { create(:procedure, :published, administrateur: admin, lien_site_web: lien_site_web) }
@ -663,96 +477,4 @@ describe Admin::ProceduresController, type: :controller do
it { expect(response.status).to eq(404) } it { expect(response.status).to eq(404) }
end end
end end
describe 'PATCH #monavis' do
let!(:procedure) { create(:procedure, administrateur: admin) }
let(:procedure_params) {
{
monavis_embed: monavis_embed
}
}
context 'when administrateur is not connected' do
before do
sign_out(admin.user)
end
subject { patch :update_monavis, params: { procedure_id: procedure.id } }
it { is_expected.to redirect_to new_user_session_path }
end
context 'when administrateur is connected' do
def update_monavis
patch :update_monavis, params: { procedure_id: procedure.id, procedure: procedure_params }
procedure.reload
end
let(:monavis_embed) {
<<-MSG
<a href="https://voxusagers.numerique.gouv.fr/Demarches/2136?&view-mode=formulaire-avis&nd_mode=en-ligne-enti%C3%A8rement&nd_source=button&key=e93e77cfd9bf7cce9d10f7a10be55730">
<img src="https://voxusagers.numerique.gouv.fr/static/bouton-bleu.svg" alt="Je donne mon avis" title="Je donne mon avis sur cette démarche" />
</a>
MSG
}
context 'when all attributes are present' do
render_views
before { update_monavis }
context 'when the embed code is valid' do
describe 'the monavis field is updated' do
subject { procedure }
it { expect(subject.monavis_embed).to eq(monavis_embed) }
end
it { expect(flash[:notice]).to be_present }
it { expect(response.body).to include "MonAvis" }
end
context 'when the embed code is valid with the original format' do
let(:monavis_embed) {
<<-MSG
<a href="https://monavis.numerique.gouv.fr/Demarches/123?&view-mode=formulaire-avis&nd_mode=en-ligne-enti%C3%A8rement&nd_source=button&key=cd4a872d475e4045666057f">
<img src="https://monavis.numerique.gouv.fr/monavis-static/bouton-blanc.png" alt="Je donne mon avis" title="Je donne mon avis sur cette démarche" />
</a>
MSG
}
describe 'the monavis field is updated' do
subject { procedure }
it { expect(subject.monavis_embed).to eq(monavis_embed) }
end
it { expect(flash[:notice]).to be_present }
it { expect(response.body).to include "MonAvis" }
end
context 'when the embed code is not valid' do
let(:monavis_embed) { 'invalid embed code' }
describe 'the monavis field is not updated' do
subject { procedure }
it { expect(subject.monavis_embed).to eq(nil) }
end
it { expect(flash[:alert]).to be_present }
it { expect(response.body).to include "MonAvis" }
end
end
context 'when procedure is published' do
let(:procedure) { create(:procedure, :published, administrateur: admin) }
subject { update_monavis }
describe 'the monavis field is not updated' do
it { expect(subject.monavis_embed).to eq monavis_embed }
end
end
end
end
end end

View file

@ -1,5 +1,17 @@
describe NewAdministrateur::ProceduresController, type: :controller do describe NewAdministrateur::ProceduresController, type: :controller do
let(:admin) { create(:administrateur) } let(:admin) { create(:administrateur) }
let(:bad_procedure_id) { 100000 }
let(:path) { 'ma-jolie-demarche' }
let(:libelle) { 'Démarche de test' }
let(:description) { 'Description de test' }
let(:organisation) { 'Organisation de test' }
let(:direction) { 'Direction de test' }
let(:cadre_juridique) { 'cadre juridique' }
let(:duree_conservation_dossiers_dans_ds) { 3 }
let(:duree_conservation_dossiers_hors_ds) { 6 }
let(:monavis_embed) { nil }
let(:lien_site_web) { 'http://mon-site.gouv.fr' }
describe '#apercu' do describe '#apercu' do
let(:procedure) { create(:procedure) } let(:procedure) { create(:procedure) }
@ -11,4 +23,282 @@ describe NewAdministrateur::ProceduresController, type: :controller do
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
end end
let(:procedure_params) {
{
path: path,
libelle: libelle,
description: description,
organisation: organisation,
direction: direction,
cadre_juridique: cadre_juridique,
duree_conservation_dossiers_dans_ds: duree_conservation_dossiers_dans_ds,
duree_conservation_dossiers_hors_ds: duree_conservation_dossiers_hors_ds,
monavis_embed: monavis_embed,
lien_site_web: lien_site_web
}
}
before do
sign_in(admin.user)
end
describe 'GET #edit' do
let(:published_at) { nil }
let(:procedure) { create(:procedure, administrateur: admin, published_at: published_at) }
let(:procedure_id) { procedure.id }
subject { get :edit, params: { id: procedure_id } }
context 'when user is not connected' do
before do
sign_out(admin.user)
end
it { is_expected.to redirect_to new_user_session_path }
end
context 'when user is connected' do
context 'when procedure exist' do
let(:procedure_id) { procedure.id }
it { is_expected.to have_http_status(:success) }
end
context 'when procedure is published' do
let(:published_at) { Time.zone.now }
it { is_expected.to have_http_status(:success) }
end
context 'when procedure doesnt exist' do
let(:procedure_id) { bad_procedure_id }
it { is_expected.to have_http_status(404) }
end
end
end
describe 'POST #create' do
context 'when all attributs are filled' do
describe 'new procedure in database' do
subject { post :create, params: { procedure: procedure_params } }
it { expect { subject }.to change { Procedure.count }.by(1) }
end
context 'when procedure is correctly save' do
before do
post :create, params: { procedure: procedure_params }
end
describe 'procedure attributs in database' do
subject { Procedure.last }
it { expect(subject.libelle).to eq(libelle) }
it { expect(subject.description).to eq(description) }
it { expect(subject.organisation).to eq(organisation) }
it { expect(subject.direction).to eq(direction) }
it { expect(subject.administrateurs).to eq([admin]) }
it { expect(subject.duree_conservation_dossiers_dans_ds).to eq(duree_conservation_dossiers_dans_ds) }
it { expect(subject.duree_conservation_dossiers_hors_ds).to eq(duree_conservation_dossiers_hors_ds) }
end
it { is_expected.to redirect_to(champs_procedure_path(Procedure.last)) }
it { expect(flash[:notice]).to be_present }
end
context 'when procedure is correctly saved' do
let(:instructeur) { admin.instructeur }
before do
post :create, params: { procedure: procedure_params }
end
describe "admin can also instruct the procedure as a instructeur" do
subject { Procedure.last }
it { expect(subject.defaut_groupe_instructeur.instructeurs).to include(instructeur) }
end
end
end
context 'when many attributs are not valid' do
let(:libelle) { '' }
let(:description) { '' }
describe 'no new procedure in database' do
subject { post :create, params: { procedure: procedure_params } }
it { expect { subject }.to change { Procedure.count }.by(0) }
describe 'no new module api carto in database' do
it { expect { subject }.to change { ModuleAPICarto.count }.by(0) }
end
end
describe 'flash message is present' do
before do
post :create, params: { procedure: procedure_params }
end
it { expect(flash[:alert]).to be_present }
end
end
end
describe 'PUT #update' do
let!(:procedure) { create(:procedure, :with_type_de_champ, administrateur: admin) }
context 'when administrateur is not connected' do
before do
sign_out(admin.user)
end
subject { put :update, params: { id: procedure.id } }
it { is_expected.to redirect_to new_user_session_path }
end
context 'when administrateur is connected' do
def update_procedure
put :update, params: { id: procedure.id, procedure: procedure_params }
procedure.reload
end
context 'when all attributs are present' do
let(:libelle) { 'Blable' }
let(:description) { 'blabla' }
let(:organisation) { 'plop' }
let(:direction) { 'plap' }
let(:duree_conservation_dossiers_dans_ds) { 7 }
let(:duree_conservation_dossiers_hors_ds) { 5 }
before { update_procedure }
describe 'procedure attributs in database' do
subject { procedure }
it { expect(subject.libelle).to eq(libelle) }
it { expect(subject.description).to eq(description) }
it { expect(subject.organisation).to eq(organisation) }
it { expect(subject.direction).to eq(direction) }
it { expect(subject.duree_conservation_dossiers_dans_ds).to eq(duree_conservation_dossiers_dans_ds) }
it { expect(subject.duree_conservation_dossiers_hors_ds).to eq(duree_conservation_dossiers_hors_ds) }
end
it { is_expected.to redirect_to(edit_admin_procedure_path id: procedure.id) }
it { expect(flash[:notice]).to be_present }
end
context 'when many attributs are not valid' do
before { update_procedure }
let(:libelle) { '' }
let(:description) { '' }
describe 'flash message is present' do
it { expect(flash[:alert]).to be_present }
end
end
context 'when procedure is brouillon' do
let(:procedure) { create(:procedure_with_dossiers, :with_path, :with_type_de_champ, administrateur: admin) }
let!(:dossiers_count) { procedure.dossiers.count }
describe 'dossiers are dropped' do
subject { update_procedure }
it {
expect(dossiers_count).to eq(1)
expect(subject.dossiers.count).to eq(0)
}
end
end
context 'when procedure is published' do
let(:procedure) { create(:procedure, :with_type_de_champ, :published, administrateur: admin) }
subject { update_procedure }
describe 'only some properties can be updated' do
it { expect(subject.libelle).to eq procedure_params[:libelle] }
it { expect(subject.description).to eq procedure_params[:description] }
it { expect(subject.organisation).to eq procedure_params[:organisation] }
it { expect(subject.direction).to eq procedure_params[:direction] }
it { expect(subject.for_individual).not_to eq procedure_params[:for_individual] }
end
end
end
end
describe 'PATCH #monavis' do
let!(:procedure) { create(:procedure, administrateur: admin) }
let(:procedure_params) {
{
monavis_embed: monavis_embed
}
}
context 'when administrateur is not connected' do
before do
sign_out(admin.user)
end
subject { patch :update_monavis, params: { id: procedure.id } }
it { is_expected.to redirect_to new_user_session_path }
end
context 'when administrateur is connected' do
def update_monavis
patch :update_monavis, params: { id: procedure.id, procedure: procedure_params }
procedure.reload
end
let(:monavis_embed) {
<<-MSG
<a href="https://monavis.numerique.gouv.fr/Demarches/123?&view-mode=formulaire-avis&nd_mode=en-ligne-enti%C3%A8rement&nd_source=button&key=cd4a872d475e4045666057f">
<img src="https://monavis.numerique.gouv.fr/monavis-static/bouton-blanc.png" alt="Je donne mon avis" title="Je donne mon avis sur cette démarche" />
</a>
MSG
}
context 'when all attributes are present' do
render_views
before { update_monavis }
context 'when the embed code is valid' do
describe 'the monavis field is updated' do
subject { procedure }
it { expect(subject.monavis_embed).to eq(monavis_embed) }
end
it { expect(flash[:notice]).to be_present }
it { expect(response.body).to include "MonAvis" }
end
context 'when the embed code is not valid' do
let(:monavis_embed) { 'invalid embed code' }
describe 'the monavis field is not updated' do
subject { procedure }
it { expect(subject.monavis_embed).to eq(nil) }
end
it { expect(flash[:alert]).to be_present }
it { expect(response.body).to include "MonAvis" }
end
end
context 'when procedure is published' do
let(:procedure) { create(:procedure, :published, administrateur: admin) }
subject { update_monavis }
describe 'the monavis field is not updated' do
it { expect(subject.monavis_embed).to eq monavis_embed }
end
end
end
end
end end

View file

@ -40,11 +40,11 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
expect(find('#procedure_for_individual_false')).not_to be_checked expect(find('#procedure_for_individual_false')).not_to be_checked
fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3' fill_in 'procedure_duree_conservation_dossiers_dans_ds', with: '3'
fill_in 'procedure_duree_conservation_dossiers_hors_ds', with: '6' fill_in 'procedure_duree_conservation_dossiers_hors_ds', with: '6'
click_on 'save-procedure' click_on 'Créer la démarche'
expect(page).to have_text('Libelle doit être rempli') expect(page).to have_text('Libelle doit être rempli')
fill_in_dummy_procedure_details fill_in_dummy_procedure_details
click_on 'save-procedure' click_on 'Créer la démarche'
expect(page).to have_current_path(champs_procedure_path(Procedure.last)) expect(page).to have_current_path(champs_procedure_path(Procedure.last))
end end
@ -59,7 +59,7 @@ feature 'As an administrateur I wanna create a new procedure', js: true do
expect(page).to have_current_path(new_admin_procedure_path) expect(page).to have_current_path(new_admin_procedure_path)
fill_in_dummy_procedure_details fill_in_dummy_procedure_details
click_on 'save-procedure' click_on 'Créer la démarche'
procedure = Procedure.last procedure = Procedure.last
procedure.update(service: create(:service)) procedure.update(service: create(:service))

View file

@ -33,6 +33,9 @@ feature 'Administrateurs can edit procedures', js: true do
expect(page).to have_field('procedure_libelle', with: procedure.libelle) expect(page).to have_field('procedure_libelle', with: procedure.libelle)
fill_in('procedure_libelle', with: 'Ma petite démarche') fill_in('procedure_libelle', with: 'Ma petite démarche')
within('.procedure-form__preview') do
expect(page).to have_content('Ma petite démarche')
end
click_on 'Enregistrer' click_on 'Enregistrer'

View file

@ -108,7 +108,7 @@ feature 'Inviting an expert:' do
expect(page).to have_text('Cet avis est confidentiel') expect(page).to have_text('Cet avis est confidentiel')
fill_in 'avis_answer', with: 'Ma réponse dexpert : cest un oui.' fill_in 'avis_answer', with: 'Ma réponse dexpert : cest un oui.'
find('.piece-justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/RIB.pdf') find('.attachment input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/RIB.pdf')
click_on 'Envoyer votre avis' click_on 'Envoyer votre avis'
expect(page).to have_content('Votre réponse est enregistrée') expect(page).to have_content('Votre réponse est enregistrée')

View file

@ -169,25 +169,19 @@ feature 'The user' do
# Mark file as scanned and clean # Mark file as scanned and clean
attachment = ActiveStorage::Attachment.last attachment = ActiveStorage::Attachment.last
attachment.blob.update(metadata: attachment.blob.metadata.merge(scanned_at: Time.zone.now, virus_scan_result: ActiveStorage::VirusScanner::SAFE)) attachment.blob.update(metadata: attachment.blob.metadata.merge(scanned_at: Time.zone.now, virus_scan_result: ActiveStorage::VirusScanner::SAFE))
within '.piece-justificative' do within('.attachment') { click_on 'rafraichir' }
click_on 'rafraichir'
end
expect(page).to have_link('file.pdf') expect(page).to have_link('file.pdf')
expect(page).to have_no_content('analyse antivirus en cours') expect(page).to have_no_content('analyse antivirus en cours')
# Replace the attachment # Replace the attachment
within '.piece-justificative' do within('.attachment') { click_on 'Remplacer' }
click_on 'Remplacer'
end
find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/RIB.pdf') find('.editable-champ-piece_justificative input[type=file]').attach_file(Rails.root + 'spec/fixtures/files/RIB.pdf')
click_on 'Enregistrer le brouillon' click_on 'Enregistrer le brouillon'
expect(page).to have_no_text('file.pdf') expect(page).to have_no_text('file.pdf')
expect(page).to have_text('RIB.pdf') expect(page).to have_text('RIB.pdf')
# Remove the attachment # Remove the attachment
within '.piece-justificative' do within('.attachment') { click_on 'Supprimer' }
click_on 'Supprimer'
end
expect(page).to have_content('La pièce jointe a bien été supprimée') expect(page).to have_content('La pièce jointe a bien été supprimée')
expect(page).to have_no_text('RIB.pdf') expect(page).to have_no_text('RIB.pdf')
end end

View file

@ -1,6 +1,6 @@
require 'spec_helper' require 'spec_helper'
describe 'admin/procedures/edit.html.haml', type: :view, vcr: { cassette_name: 'admin_procedure_edit' } do describe 'new_administrateur/procedures/edit.html.haml' do
let(:logo) { Rack::Test::UploadedFile.new("./spec/fixtures/files/logo_test_procedure.png", 'image/png') } let(:logo) { Rack::Test::UploadedFile.new("./spec/fixtures/files/logo_test_procedure.png", 'image/png') }
let(:procedure) { create(:procedure, logo: logo, lien_site_web: 'http://some.website') } let(:procedure) { create(:procedure, logo: logo, lien_site_web: 'http://some.website') }
@ -11,7 +11,7 @@ describe 'admin/procedures/edit.html.haml', type: :view, vcr: { cassette_name: '
context 'when procedure logo is present' do context 'when procedure logo is present' do
it 'display on the page' do it 'display on the page' do
expect(rendered).to have_selector('#preview_procedure_logo') expect(rendered).to have_selector('.procedure-logos')
end end
end end
end end

View file

@ -2,33 +2,37 @@ require 'rails_helper'
describe 'shared/attachment/_update.html.haml', type: :view do describe 'shared/attachment/_update.html.haml', type: :view do
let(:champ) { build(:champ_piece_justificative, dossier: create(:dossier)) } let(:champ) { build(:champ_piece_justificative, dossier: create(:dossier)) }
let(:attachment) { nil } let(:attached_file) { champ.piece_justificative_file }
let(:virus_scan_result) { nil }
let(:user_can_destroy) { false } let(:user_can_destroy) { false }
subject do subject do
form_for(champ.dossier) do |form| form_for(champ.dossier) do |form|
render 'shared/attachment/update', { render 'shared/attachment/edit', {
attachment: attachment, form: form,
user_can_destroy: user_can_destroy, attached_file: attached_file,
form: form accept: 'image/png',
user_can_destroy: user_can_destroy
} }
end end
end end
context 'when there is no attached file' do
before do
champ.piece_justificative_file.purge
end
it 'renders a form field for uploading a file' do it 'renders a form field for uploading a file' do
expect(subject).to have_selector('input[type=file]:not(.hidden)') expect(subject).to have_selector('input[type=file]:not(.hidden)')
end end
end
context 'when there is a attached file' do context 'when there is a attached file' do
let(:attachment) { champ.piece_justificative_file.attachment }
it 'renders a form field for uploading a file' do it 'renders a form field for uploading a file' do
expect(subject).to have_selector('input[type=file]:not(.hidden)') expect(subject).to have_selector('input[type=file]:not(.hidden)')
end end
it 'does not renders a link to the unsaved file' do it 'does not renders a link to the unsaved file' do
expect(subject).not_to have_content(attachment.filename.to_s) expect(subject).not_to have_content(attached_file.filename.to_s)
end end
it 'doesnt render action buttons' do it 'doesnt render action buttons' do
@ -40,7 +44,7 @@ describe 'shared/attachment/_update.html.haml', type: :view do
before { champ.save! } before { champ.save! }
it 'renders a link to the file' do it 'renders a link to the file' do
expect(subject).to have_content(attachment.filename.to_s) expect(subject).to have_content(attached_file.filename.to_s)
end end
it 'renders action buttons' do it 'renders action buttons' do