diff --git a/app/assets/stylesheets/pages/cas_usages-show.css b/app/assets/stylesheets/pages/cas_usages-show.css new file mode 100644 index 0000000000..8b504ab873 --- /dev/null +++ b/app/assets/stylesheets/pages/cas_usages-show.css @@ -0,0 +1,55 @@ +.cas-usage-section { + padding: 3rem 0; +} + +.cas-usage-section.even:nth-of-type(even) { + background-color: var(--background-alt-grey); +} + +.cas-usage-section.odd:nth-of-type(odd) { + background-color: var(--background-alt-grey); +} + +.cas-usage-section:first-of-type { + padding: 0 0 3rem 0; +} + +.viewer-of-content { + border: 1px solid lightgray; + border-radius: 1rem; + padding: 2rem; + background-color: white; +} + +.title-with-modify-button { + display: flex; + flex-direction: row-reverse; + flex-wrap: wrap; + justify-content: right; +} + +.title-with-modify-button .title { + flex-grow: 1; +} + +.cas-usage-properties { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +.cas-usage-property { + border: 1px lightgray solid; + padding: 0.5rem 1rem; + border-radius: 5px; +} + +.header-count-badges { + display: flex; + flex-direction: column; + justify-content: center; +} + +.bordered-link-box { + border: 1px solid #000091; +} \ No newline at end of file diff --git a/app/components/instructor_menu_component.html.erb b/app/components/instructor_menu_component.html.erb index e5aa3c95e0..b2fda88903 100644 --- a/app/components/instructor_menu_component.html.erb +++ b/app/components/instructor_menu_component.html.erb @@ -4,14 +4,6 @@ -<% if show_drafts %> -
  • - - <%= t('layouts.header.menu.instruction.instructor_authorization_requests') %> - -
  • -<% end %> - <% if show_templates %>
  • diff --git a/app/controllers/instruction/cas_usages_controller.rb b/app/controllers/instruction/cas_usages_controller.rb index 5cd9ddbba9..4f96478e1e 100644 --- a/app/controllers/instruction/cas_usages_controller.rb +++ b/app/controllers/instruction/cas_usages_controller.rb @@ -1,15 +1,40 @@ class Instruction::CasUsagesController < Instruction::AbstractCatalogueController before_action :set_formulaire + def index + @cas_usages = @formulaire.available_forms + @demandes_counts = counts_by_form_uid(AuthorizationRequest.where(state: :submitted)) + @habilitations_counts = counts_by_form_uid(Authorization.where(state: :active)) + + + @formulaire_demandes_count = @formulaire.authorization_request_class.where(state: :submitted).count + @formulaire_habilitations_counts = Authorization.where(authorization_request_class: @formulaire.authorization_request_class.to_s).where(state: :active).count + end + def show @cas_usage = @formulaire.available_forms.find { |form| form.uid == params[:uid] } raise ActiveRecord::RecordNotFound unless @cas_usage authorize [:instruction, @cas_usage], :show? + + @authorization_request = @cas_usage.authorization_request_class.new(form_uid: @cas_usage.uid).tap do |ar| + ar.assign_attributes(@cas_usage.initialize_with) + end.decorate + + @cas_usage_demandes_count = AuthorizationRequest.where(state: :submitted).where(form_uid: @cas_usage.uid).count + @cas_usage_habilitations_count = Authorization.where(state: :active).where(form_uid: @cas_usage.uid).count end private + def layout_name + 'wide_container' + end + + def counts_by_form_uid(model) + model.where(form_uid: @cas_usages.map(&:uid)).group(:form_uid).count + end + def set_formulaire @formulaire = AuthorizationDefinition.find(params.expect(:formulaire_id)) raise ActiveRecord::RecordNotFound unless @formulaire.provider_slug == @data_provider.slug diff --git a/app/controllers/instruction/dashboard_controller.rb b/app/controllers/instruction/dashboard_controller.rb index 49080ff438..f2dd38cb0a 100644 --- a/app/controllers/instruction/dashboard_controller.rb +++ b/app/controllers/instruction/dashboard_controller.rb @@ -7,6 +7,7 @@ class Instruction::DashboardController < Instruction::AbstractAuthorizationReque Tab = Data.define(:id, :path, :count) def show + @fomulaires_management_path = fomulaires_management_path case params[:id] when 'habilitations' search_object = Instruction::Search::DashboardHabilitationsSearch.new( @@ -80,4 +81,14 @@ def search_record_path(record) def search_terms_is_a_possible_id? Instruction::Search::DashboardSearch.search_terms_is_a_possible_id?(params) end + + def fomulaires_management_path + data_providers = policy_scope([:instruction, DataProvider]).to_a + + if data_providers.count == 1 + instruction_formulaires_path(provider_slug: data_providers.first) + else + instruction_data_providers_path + end + end end diff --git a/app/controllers/instruction/emails_automatiques_controller.rb b/app/controllers/instruction/emails_automatiques_controller.rb new file mode 100644 index 0000000000..cc6eeca723 --- /dev/null +++ b/app/controllers/instruction/emails_automatiques_controller.rb @@ -0,0 +1,24 @@ +class Instruction::EmailsAutomatiquesController < Instruction::AbstractCatalogueController + before_action :set_formulaire + + def show + @emails = AutomaticEmailsCatalog.new(@formulaire).build + @formulaire_demandes_count = @formulaire.authorization_request_class.where(state: :submitted).count + @formulaire_habilitations_count = Authorization.where(authorization_request_class: @formulaire.authorization_request_class.to_s).where(state: :active).count + end + + private + + def set_formulaire + @formulaire = AuthorizationDefinition.find(params.expect(:formulaire_id)) + raise ActiveRecord::RecordNotFound unless @formulaire.provider_slug == @data_provider.slug + + authorize [:instruction, @formulaire], :show? + rescue StaticApplicationRecord::EntryNotFound + raise ActiveRecord::RecordNotFound + end + + def layout_name + 'wide_container' + end +end diff --git a/app/controllers/instruction/formulaires_controller.rb b/app/controllers/instruction/formulaires_controller.rb index 88b41d4508..28f4d74d7c 100644 --- a/app/controllers/instruction/formulaires_controller.rb +++ b/app/controllers/instruction/formulaires_controller.rb @@ -2,28 +2,48 @@ class Instruction::FormulairesController < Instruction::AbstractCatalogueControl def index @formulaires = @data_provider.authorization_definitions .select { |definition| permitted_definition?(definition) } + + classes = @formulaires.map(&:authorization_request_class).map(&:to_s) + @demandes_counts = AuthorizationRequest.where(state: :submitted).where(type: classes).group(:type).count + @habilitations_counts = Authorization.where(state: :active).where(authorization_request_class: classes).group(:authorization_request_class).count end def show @formulaire = AuthorizationDefinition.find(params.expect(:id)) raise ActiveRecord::RecordNotFound unless @formulaire.provider_slug == @data_provider.slug + # on utilise la demande libre pour montrer le formulaire + @cas_usage = @formulaire.default_form + @authorization_request = @formulaire.authorization_request_class.new(form_uid: @cas_usage.uid).tap do |ar| + ar.assign_attributes(@cas_usage.initialize_with) + end.decorate + authorize [:instruction, @formulaire], :show? - @cas_usages = @formulaire.available_forms - @demandes_counts = counts_by_form_uid(AuthorizationRequest) - @habilitations_counts = counts_by_form_uid(Authorization) + @cas_usages_count = @formulaire.available_forms.count + @emails_automatiques_count = emails_automatiques_count + @formulaire_demandes_count = @formulaire.authorization_request_class.where(state: :submitted).count + @formulaire_habilitations_count = Authorization.where(authorization_request_class: @formulaire.authorization_request_class.to_s).where(state: :active).count rescue StaticApplicationRecord::EntryNotFound raise ActiveRecord::RecordNotFound end private - def permitted_definition?(definition) - Instruction::AuthorizationDefinitionPolicy.new(pundit_user, definition).show? + def layout_name + 'wide_container' + end + + def counts_by_definition(model) + + model.where(type: classes).group(:type).count + end + + def emails_automatiques_count + AutomaticEmailsCatalog.new(@formulaire).build.count end - def counts_by_form_uid(model) - model.where(form_uid: @cas_usages.map(&:uid)).group(:form_uid).count + def permitted_definition?(definition) + Instruction::AuthorizationDefinitionPolicy.new(pundit_user, definition).show? end end diff --git a/app/services/automatic_emails_catalog.rb b/app/services/automatic_emails_catalog.rb new file mode 100644 index 0000000000..fdd74c61c1 --- /dev/null +++ b/app/services/automatic_emails_catalog.rb @@ -0,0 +1,242 @@ +class AutomaticEmailsCatalog + EVENTS = %w[submit approve refuse request_changes revoke].freeze + + SUBJECT_DUMMY_VALUES = { + authorization_request_id: 'N°0001', + authorization_request_name: 'Ma demande d\'habilitation', + api_name: 'API', + organization_name: 'Mon organisation', + }.freeze + + GDPR_CONTACTS = %i[responsable_traitement delegue_protection_donnees].freeze + + AutomaticEmailEntry = Struct.new(:event_name, :recipient_type, :subject, :body_text, :recipient_emails, keyword_init: true) + PreviewAuthorization = Struct.new(:formatted_id) + + def initialize(definition) + @definition = definition + end + + def build + spy = build_spy_notifier + + EVENTS.flat_map do |event| + spy.clear_captures + spy.public_send(event, {}) + spy.captures.map { |capture| build_entry(event, capture) } + rescue StandardError + [] + end + end + + private + + def build_entry(event_name, capture) + template = capture[:template_path] || resolve_template_path(capture[:mailer_class], capture[:mailer_action]) + subject = capture[:subject] || render_subject(capture[:mailer_class], capture[:mailer_action]) + + AutomaticEmailEntry.new( + event_name:, + recipient_type: capture[:recipient_type], + recipient_emails: capture[:recipient_emails], + subject:, + body_text: render_body(template, extra_assigns: capture.fetch(:extra_assigns, {})), + ) + end + + def render_subject(mailer_class, mailer_action) + key = subject_locale_key(mailer_class, mailer_action) + I18n.t(key, **SUBJECT_DUMMY_VALUES) + rescue I18n::MissingInterpolationArgument + I18n.t(key) + end + + def subject_locale_key(mailer_class, mailer_action) + if mailer_class == Instruction::AuthorizationRequestMailer + "instruction.authorization_request_mailer.#{mailer_action}.subject" + else + "authorization_request_mailer.#{mailer_action}.subject" + end + end + + def render_body(template, extra_assigns: {}) + return nil unless template + + ar = PreviewAuthorizationRequest.new(@definition) + + ApplicationController.render( + template:, + assigns: { authorization_request: ar }.merge(extra_assigns), + formats: [:text], + ) + rescue StandardError + nil + end + + def resolve_template_path(mailer_class, mailer_action) + return nil unless mailer_class && mailer_action + return instruction_template_path(mailer_action) if mailer_class == Instruction::AuthorizationRequestMailer + + standard_template_path(mailer_action) + end + + def instruction_template_path(mailer_action) + path = "instruction/authorization_request_mailer/#{mailer_action}" + template_exists?(path) ? path : nil + end + + def standard_template_path(mailer_action) + kind_specific = "authorization_request_mailer/#{@definition.id}/#{mailer_action}" + generic = "authorization_request_mailer/#{mailer_action}" + + if template_exists?(kind_specific) then kind_specific + elsif template_exists?(generic) then generic + end + end + + def template_exists?(path) + ApplicationController.new.lookup_context.exists?(path, [], false) + end + + def build_spy_notifier + notifier_class = resolve_notifier_class + ar_stub = @definition.authorization_request_class.new + spy_class = build_spy_class(notifier_class) + spy = spy_class.new(ar_stub) + spy.definition = @definition + spy + end + + def build_spy_class(notifier_class) + spy = build_base_spy_class(notifier_class) + add_hubee_spy_overrides(spy) if notifier_class <= HubEENotifier + spy + end + + def build_base_spy_class(notifier_class) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + Class.new(notifier_class) do + attr_reader :captures + attr_accessor :definition + + def initialize(authorization_request) + super + @captures = [] + end + + def clear_captures + @captures = [] + end + + protected + + def email_notification(event_name, _params) + @captures << { recipient_type: :applicant, mailer_class: AuthorizationRequestMailer, mailer_action: event_name.to_s } + end + + def email_notification_with_reopening(base_event, _params, mailer: AuthorizationRequestMailer) + recipient_type = mailer == Instruction::AuthorizationRequestMailer ? :instructors : :applicant + @captures << { recipient_type:, mailer_class: mailer, mailer_action: base_event.to_s } + end + + def deliver_gdpr_emails + AutomaticEmailsCatalog::GDPR_CONTACTS.each do |contact| + @captures << { + recipient_type: contact, + subject: I18n.t( + "gdpr_contact_mailer.#{contact}.subject", + authorization_request_contact_kind: I18n.t("authorization_request.contacts.#{contact}"), + authorization_request_name: AutomaticEmailsCatalog::SUBJECT_DUMMY_VALUES[:authorization_request_name], + ), + template_path: "gdpr_contact_mailer/#{contact}", + } + end + end + + def notify_france_connect + @captures << { + recipient_type: :france_connect, + recipient_emails: ['support.partenaires@franceconnect.gouv.fr'], + subject: "[DataPass] nouveaux scopes pour « #{AutomaticEmailsCatalog::SUBJECT_DUMMY_VALUES[:organization_name]} - #{AutomaticEmailsCatalog::SUBJECT_DUMMY_VALUES[:authorization_request_id]} »", + template_path: 'france_connect_mailer/new_scopes', + extra_assigns: { france_connect_authorization_request: AutomaticEmailsCatalog::PreviewAuthorizationRequest.new(definition) }, + } + end + + def notify_dgfip_apim(_params) + apim_emails = Rails.application.credentials.dgfip_apim_emails || [] + api_emails = (Rails.application.credentials.dgfip_api_specific_emails || {})[definition.id.to_sym] || [] + + @captures << { + recipient_type: :dgfip_apim, + recipient_emails: apim_emails + api_emails, + subject: I18n.t('dgfip_apim_mailer.approve.subject', authorization_request_id: AutomaticEmailsCatalog::SUBJECT_DUMMY_VALUES[:authorization_request_id]), + template_path: 'dgfip/apim_mailer/approve', + extra_assigns: { + authorization: AutomaticEmailsCatalog::PreviewAuthorization.new('H-0001'), + reopening: false, + stage_label: '[BAS ou PROD]', + }, + } + end + end + end + + def add_hubee_spy_overrides(spy) + spy.class_eval do + private + + def applicant_and_administrateur_metier_are_different? + true + end + + def notify_administrateur_metier + @captures << { + recipient_type: :hubee_administrateur_metier, + subject: HubEEMailer.new.subject_for(kind), + template_path: "hubee_mailer/administrateur_metier_#{kind}", + } + end + end + end + + def resolve_notifier_class + "#{@definition.authorization_request_class.to_s.demodulize}Notifier".constantize + rescue NameError + BaseNotifier + end + + class PreviewAuthorizationRequest + Applicant = Struct.new(:full_name, :email) + Organization = Struct.new(:name) + Reason = Struct.new(:reason, :present?) + + def initialize(definition) + @definition = definition + end + + attr_reader :definition + + delegate :name, to: :definition + + def id = 0 + def to_param = '0' + def model_name = ActiveModel::Name.new(AuthorizationRequest) + def formatted_id = 'N°0001' + def reopening? = false + def latest_authorization = nil + def last_submitted_at = Time.current + def applicant = Applicant.new('[Nom du demandeur]', 'demandeur@example.org') + def organization = Organization.new('[Organisation]') + def denial = Reason.new('[Motif de refus]') + def revocation = Reason.new('[Motif de révocation]') + def modification_request = Reason.new('[Demande de modification]', false) + def authorization_request_revocation_reason = '[Motif de révocation]' + def with_france_connect? = false + def responsable_traitement_given_name = '[Prénom Resp. Traitement]' + def delegue_protection_donnees_given_name = '[Prénom DPD]' + def administrateur_metier_given_name = '[Prénom Admin]' + def administrateur_metier_family_name = '[Nom Admin]' + def administrateur_metier_email = '[Email Admin]' + def scopes = [] + end +end diff --git a/app/services/skip_links_implemented_checker.rb b/app/services/skip_links_implemented_checker.rb index ffddd6199d..7effdce0bd 100644 --- a/app/services/skip_links_implemented_checker.rb +++ b/app/services/skip_links_implemented_checker.rb @@ -124,6 +124,7 @@ class SkipLinksImplementedChecker instruction/formulaires#index instruction/formulaires#show instruction/cas_usages#show + instruction/emails_automatiques#show developers/open_api#show developers/oauth_applications#index diff --git a/app/views/authorization_request_forms/_new.html.erb b/app/views/authorization_request_forms/_new.html.erb new file mode 100644 index 0000000000..5bfb8cfb77 --- /dev/null +++ b/app/views/authorization_request_forms/_new.html.erb @@ -0,0 +1,65 @@ + + + +
    + <% if @start_next_stage || policy(authorization_request_form).create? %> +
    + <% if authorization_request_form.name.present? %> + <%= simple_format t(".organization.start_text_with_form_name", + organization_name: current_organization.name, + organization_siret: current_organization.siret, + authorization_request_form_name: authorization_request_form.name), + {}, wrapper_tag: 'div' %> + <% else %> + <%= simple_format t(".organization.start_text", + organization_name: current_organization.name, + organization_siret: current_organization.siret), + {}, wrapper_tag: 'div' %> + <% end %> +
    + <% else %> + <% authorization_request = authorization_request_form.active_authorization_requests_for(current_organization).first %> + <%= render partial: "authorization_request_forms/unicity_callout", locals: { authorization_request: } if authorization_request.present? %> + <% end %> + + <% if authorization_request_form.introduction.present? %> +
    +
    + <%= t(".intro.title") %> +
    + <%= authorization_request_form.introduction.html_safe %> +
    + <% end %> + + <% if authorization_request_form.multiple_steps? %> + <% if @authorization_request.present? || policy(authorization_request_form).new? %> +
    + <%= t('.multiple_steps.title') %> +
    +

    + <%= t('.multiple_steps.description') %> +

    +
      + <% authorization_request_form.steps.each do |step| %> +
    1. + <%= authorization_request_step_name(authorization_request_form.authorization_request_class.new, step[:name]) %> +
    2. + <% end %> +
    + + <% else %> + + <% end %> + <% else %> +
    +
    + <%= t(".single_form.title") %> +
    + +

    + <%= simple_format t(".single_form.description") %> +

    +
    + + <% end %> +
    diff --git a/app/views/instruction/cas_usages/_header.html.erb b/app/views/instruction/cas_usages/_header.html.erb new file mode 100644 index 0000000000..d9676f91c6 --- /dev/null +++ b/app/views/instruction/cas_usages/_header.html.erb @@ -0,0 +1,43 @@ +
    +
    +
    + <%= link_to "Cas d'usages de #{@formulaire.name_with_stage}", + instruction_formulaire_cas_usages_path(provider_slug: @data_provider.slug, formulaire_id: @formulaire.id), + class: 'fr-link fr-icon-arrow-left-line fr-link--icon-left' %> +
    +
    + <% if @data_provider.logo.attached? %> +
    + <%= image_tag @data_provider.logo, alt: "#{@data_provider.name}, fournisseur de données", style: 'max-height: 80px; max-width: 160px;' %> +
    + <% end %> +
    +

    + <%= @cas_usage.name_with_service_provider %> +

    +

    + <%= t('instruction.cas_usages.show.identification') %> : + <%= @cas_usage.uid %> +

    +
    + +
    + <%= render 'instruction/shared/stats_badges', habilitations_count: @cas_usage_habilitations_count || 0, demandes_count: @cas_usage_demandes_count || 0 %> +
    +
    + +
    +
    + <%= link_to "Initier une demande pour autrui", start_instruction_instructor_draft_requests_path(form_uid: @cas_usage.uid), class: "fr-btn fr-btn-primary" %> +
    +
    +
    + <%= link_to "Démarrer une nouvelle demande", new_authorization_request_form_path(form_uid: @cas_usage.uid), class: "fr-btn fr-btn--secondary"%> +
    +
    +
    +
    +
    diff --git a/app/views/instruction/cas_usages/index.html.erb b/app/views/instruction/cas_usages/index.html.erb new file mode 100644 index 0000000000..bcc06662ce --- /dev/null +++ b/app/views/instruction/cas_usages/index.html.erb @@ -0,0 +1,41 @@ +<%= render 'instruction/formulaires/header', formulaire_back_link: true %> + +
    +
    +

    <%= t('.title') %>

    + <% if @cas_usages.any? %> +
    + <% @cas_usages.each do |cas_usage| %> + <% demandes_count = @demandes_counts.fetch(cas_usage.uid, 0) %> + <% habilitations_count = @habilitations_counts.fetch(cas_usage.uid, 0) %> +
    + +
    + <% end %> +
    + <% else %> +

    <%= t('.empty_cas_usages') %>

    + <% end %> +
    +
    diff --git a/app/views/instruction/cas_usages/show.html.erb b/app/views/instruction/cas_usages/show.html.erb index 1c19efee8b..06bf74756b 100644 --- a/app/views/instruction/cas_usages/show.html.erb +++ b/app/views/instruction/cas_usages/show.html.erb @@ -1,421 +1,70 @@ <% set_title! t('page_titles.instruction.cas_usages_show', cas_usage_name: @cas_usage.name_with_service_provider, provider_name: @data_provider.name) %> -<%= render 'instruction/data_providers/header' %> +<%= render 'instruction/cas_usages/header' %> -
    -

    - <%= link_to t('.back'), - instruction_formulaire_path(provider_slug: @data_provider.slug, id: @formulaire.id), - class: 'fr-link fr-icon-arrow-left-line fr-link--icon-left' %> -

    -

    - <%= @cas_usage.name_with_service_provider %> - <% if @cas_usage.default %> - - <%= t('.default_badge') %> - +
    +
    + <%= render 'instruction/shared/title_with_modify_button' do %> +

    Configuration

    <% end %> - <% if @cas_usage.inherited?(:name) %> - - <%= t('.inherited_badge') %> - - <% end %> -

    -

    - <%= t('.sections.identification') %> : - <%= @cas_usage.uid %> - — <%= t('instruction.formulaires.show.title') %> : - <%= link_to @formulaire.name_with_stage, - instruction_formulaire_path(provider_slug: @data_provider.slug, id: @formulaire.id), - class: 'fr-link' %> -

    - -
    -

    - <%= t('.sections.description') %> - <% if @cas_usage.inherited?(:description) %> - <%= t('.inherited_badge') %> - <% end %> -

    - <% if @cas_usage.description.present? %> - <%= render 'rendered_html_tabs', - id_prefix: 'description', - rendered: sanitize(@cas_usage.description, tags: %w[strong em b i u br a p ul li]), - source: @cas_usage.description %> - <% else %> -

    <%= t('.empty_description') %>

    - <% end %> -
    - -
    -

    - <%= t('.sections.introduction') %> - <% if @cas_usage.inherited?(:introduction) %> - <%= t('.inherited_badge') %> - <% end %> -

    - <% if @cas_usage.introduction.present? %> - <%= render 'rendered_html_tabs', - id_prefix: 'introduction', - rendered: sanitize(@cas_usage.introduction, tags: %w[strong em b i u br a p ul li div]), - source: @cas_usage.introduction %> - <% else %> -

    <%= t('.empty_introduction') %>

    - <% end %> -
    - -
    -

    <%= t('.sections.flags') %>

    -
    - - - - - - - - - - - - - - - - - - - - - -
    PropriétéValeur
    <%= t('.flags.public') %><%= t(".#{@cas_usage.public}") %>
    <%= t('.flags.startable_by_applicant') %><%= t(".#{@cas_usage.startable_by_applicant}") %>
    - <%= t('.flags.single_page_view') %> - - - - - <% if @cas_usage.single_page_view.present? %> - <%= @cas_usage.single_page_view %> - <% else %> - <%= t('.flags.single_page_view_default') %> - <% end %> -
    -
    -
    - -
    -

    - <%= t('.sections.flow') %> - <% if @cas_usage.inherited?(:steps) %> - <%= t('.inherited_badge') %> - <% end %> -

    - -
    -
      -
    • - -
    • -
    • - -
    • -
    -
    - <% catalog_names = @formulaire.blocks.map { |b| b[:name] || b['name'] } %> - <% static_names = @cas_usage.static_blocks.map { |b| b[:name] || b['name'] }.to_set %> - <% step_names = @cas_usage.steps.map { |s| s[:name] || s['name'] }.to_set %> - <% catalog_items = catalog_names.map { |n| [n, static_names.include?(n) ? :static : :editable, false] } %> - <% extras = ((static_names | step_names) - catalog_names.to_set).map { |n| [n, static_names.include?(n) ? :static : :editable, true] } %> - <% ordered_items = catalog_items + extras.to_a %> - - <% mode_label = @cas_usage.steps.empty? ? :single_page : :multi_step %> -

    - <%= t(".flow.mode.#{mode_label}_intro") %> - <% if mode_label == :single_page %> - <%= t('.flow.single_page_badge') %> - <%= @cas_usage.single_page_view.presence || @cas_usage.uid.underscore %> +

    + + +
    + <% if @cas_usage.service_provider %> + <% if @cas_usage.service_provider.try(:editor?) %> + Editeur : + <% elsif @cas_usage.service_provider.try(:saas?) %> + Saas : + <% else %> + Fournisseur de service : <% end %> -

    - - <% if ordered_items.any? %> -
      - <% ordered_items.each_with_index do |(name, state, out_of_catalog), idx| %> -
    1. - <%= idx + 1 %> - <% if state == :static %> - - <%= name %> - - <% else %> - <%= name %> - <% end %> - <% if out_of_catalog %> - - <%= t('.flow.out_of_catalog') %> - - <% end %> -
    2. - <% end %> -
    + <%= @cas_usage.service_provider.name %> <% else %> -

    <%= t('.flow.empty_catalog') %>

    +
    <%= t('.no_service_provider') %>
    <% end %>
    - -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= t('.flow.tabs.data_column') %><%= t('.flow.tabs.data_value') %>
    - formulaire.blocks - - - - - <% catalog = @formulaire.blocks.map { |b| b[:name] || b['name'] } %> - <% if catalog.any? %> -
      - <% catalog.each do |name| %> -
    1. <%= name %>
    2. - <% end %> -
    - <% else %> - <%= t('.flow.tabs.empty_array') %> - <% end %> -
    steps - <% step_names = @cas_usage.steps.map { |s| s[:name] || s['name'] } %> - <% if step_names.any? %> -
      - <% step_names.each do |name| %> -
    1. <%= name %>
    2. - <% end %> -
    - <% else %> - <%= t('.flow.tabs.empty_array') %> - <% end %> -
    static_blocks - <% block_names = @cas_usage.static_blocks.map { |b| b[:name] || b['name'] } %> - <% if block_names.any? %> -
      - <% block_names.each do |name| %> -
    • <%= name %>
    • - <% end %> -
    - <% else %> - <%= t('.flow.tabs.empty_array') %> - <% end %> -
    single_page_view - <% if @cas_usage.single_page_view.present? %> - <%= @cas_usage.single_page_view %> - <% else %> - nil - <% end %> -
    -
    + +
    + Visible dans la liste des cas d'usages : + <%= t(".#{@cas_usage.public}") %>
    -
    -
    - -
    -

    <%= t('.sections.scopes_config') %>

    - <% if @formulaire.scopes.blank? %> -

    <%= t('.empty_scopes_config') %>

    - <% else %> - <% disabled_set = (@cas_usage.scopes_config[:disabled] || []).to_set %> - <% hidden_set = (@cas_usage.scopes_config[:hide] || []).to_set %> - <% displayed_whitelist = @cas_usage.scopes_config[:displayed].presence %> - <% preset_set = (@cas_usage.initialize_with[:scopes] || []).to_set %> -
    -
      -
    • - -
    • -
    • - -
    • -
    +
    + <%= t('.flags.startable_by_applicant') %> : + <%= t(".#{@cas_usage.startable_by_applicant}") %> +
    -
    -

    <%= t('.scopes_config_intro') %>

    -
      -
    • <%= t('.scope_states.displayed') %>
    • -
    • <%= t('.scope_states.disabled') %>
    • -
    • <%= t('.scope_states.hidden') %>
    • -
    • - - - <%= t('.scope_states.preset') %> - -
    • -
    +
    + <%= t('.flags.single_page_view') %> : - <% @formulaire.scopes.group_by(&:group).each do |group_name, group_scopes| %> -
    - <% if group_name.present? %> -

    <%= group_name %>

    - <% end %> -
      - <% group_scopes.each do |scope| %> - <% state = if hidden_set.include?(scope.value) || (displayed_whitelist && !displayed_whitelist.include?(scope.value)) - :hidden - elsif disabled_set.include?(scope.value) - :disabled - else - :displayed - end %> - <% classes, style = case state - when :displayed then ['fr-tag fr-icon-eye-line fr-tag--icon-left', nil] - when :disabled then ['fr-tag fr-tag--brown-cafe-creme fr-icon-lock-line fr-tag--icon-left', nil] - when :hidden then ['fr-tag fr-icon-eye-off-line fr-tag--icon-left', 'opacity: 0.55;'] - end %> -
    • - - <% if preset_set.include?(scope.value) %> - - <% end %> - <%= scope.name.presence || scope.value %> - -
    • - <% end %> -
    -
    + + <% if @cas_usage.single_page_view.present? %> + Oui (<%= @cas_usage.single_page_view %>) + <% else %> + <%= t('.flags.single_page_view_default') %> <% end %> -
    + +
    +
    -
    - <% code_data = { - 'scopes_config' => @cas_usage.scopes_config.deep_stringify_keys, - 'preselected_scopes' => (@cas_usage.initialize_with[:scopes] || []), - } %> -
    <%= code_data.to_yaml(line_width: -1).sub(/\A---\n/, '') %>
    +
    +
    -

    <%= t('.formulaire_scopes_intro') %>

    - <% formulaire_scopes_dump = { - 'formulaire_scopes' => @formulaire.scopes.map { |s| - { - 'name' => s.name, - 'value' => s.value, - 'group' => s.group, - 'link' => s.link, - 'included' => s.included? || nil, - 'disabled' => s.disabled? || nil, - 'deprecated' => (s.deprecated_date if s.deprecated?), - }.compact - }, - } %> -
    <%= formulaire_scopes_dump.to_yaml(line_width: -1).sub(/\A---\n/, '') %>
    -
    - +
    +
    + <%= render 'instruction/shared/title_with_modify_button' do %> +

    Aperçu dans la liste des cas d'usages

    <% end %> -
    - <% initial_data = @cas_usage.initialize_with.except(:scopes) %> -
    -

    <%= t('.sections.initial_data') %>

    - <% if initial_data.blank? %> -

    <%= t('.empty_initial_data') %>

    - <% else %> -
    -
      -
    • - -
    • -
    • - -
    • -
    +
    + <%= render 'authorization_request_forms/authorization_request_form_card', authorization_request_form: @cas_usage %> +
    +
    +
    -
    -

    <%= t('.initial_data_tabs.form_intro') %>

    - <% initial_data.each do |key, value| %> - <% label = t("activerecord.attributes.authorization_request.#{key}", default: key.to_s.humanize) %> - <% input_id = "initial-#{@cas_usage.uid}-#{key}".parameterize %> -
    - - <% if key.to_s == 'modalities' && value.is_a?(Array) %> -
      - <% value.each do |modality| %> -
    • <%= modality %>
    • - <% end %> -
    - <% else %> - <% case value - when String %> - <% if value.match?(%r{\Ahttps?://}) %> - - <% elsif value.include?("\n") || value.length > 80 %> - - <% else %> - - <% end %> - <% when TrueClass, FalseClass %> - " readonly> - <% when Numeric %> - - <% else %> -
    <%= JSON.pretty_generate(value) %>
    - <% end %> - <% end %> -
    - <% end %> -
    +<%= render 'instruction/shared/formulaire_wysiwyg', authorization_request: @authorization_request %> -
    -
    <%= initial_data.deep_stringify_keys.to_yaml(line_width: -1).sub(/\A---\n/, '') %>
    -
    - - <% end %> - -
    -

    <%= t('.sections.service_provider') %>

    - <% if @cas_usage.service_provider %> -

    - <%= @cas_usage.service_provider.name %> - <% if @cas_usage.service_provider.try(:editor?) %> - éditeur - <% end %> -

    - <% else %> -

    <%= t('.no_service_provider') %>

    - <% end %> -
    - diff --git a/app/views/instruction/dashboard/show.html.erb b/app/views/instruction/dashboard/show.html.erb index a84eca00e0..6e0af63231 100644 --- a/app/views/instruction/dashboard/show.html.erb +++ b/app/views/instruction/dashboard/show.html.erb @@ -2,8 +2,18 @@ <%= render 'shared/dashboard_skip_links' %> -
    -

    <%= t('.title') %>

    +
    + +

    <%= t('.title') %>

    + +
    +
    + <%= link_to "Gérer mes formulaires", @fomulaires_management_path, class: "fr-btn fr-btn--secondary" %> +
    +
    + <%= link_to "Initier une demande pour autrui", instruction_instructor_draft_requests_path, class: "fr-btn fr-btn-primary" %> +
    +
    diff --git a/app/views/instruction/data_providers/_header.html.erb b/app/views/instruction/data_providers/_header.html.erb index 04302ea918..f36da2fb8c 100644 --- a/app/views/instruction/data_providers/_header.html.erb +++ b/app/views/instruction/data_providers/_header.html.erb @@ -1,5 +1,10 @@
    +
    + <%= link_to t('instruction.data_providers.index.title'), + instruction_data_providers_path, + class: 'fr-link fr-icon-arrow-left-line fr-link--icon-left' %> +
    <% if @data_provider.logo.attached? %>
    @@ -7,15 +12,8 @@
    <% end %>
    -

    - <%= link_to t('instruction.data_providers.title'), - instruction_data_providers_path, - class: 'fr-link fr-icon-arrow-left-line fr-link--icon-left' %> -

    - <%= link_to @data_provider.name, - instruction_formulaires_path(provider_slug: @data_provider.slug), - class: 'fr-link fr-link--lg' %> + <%= @data_provider.name %>

    <% if @data_provider.link.present? %>

    diff --git a/app/views/instruction/data_providers/index.html.erb b/app/views/instruction/data_providers/index.html.erb index 5c305eae60..65df0f3fcd 100644 --- a/app/views/instruction/data_providers/index.html.erb +++ b/app/views/instruction/data_providers/index.html.erb @@ -1,51 +1,42 @@ <% set_title! t('page_titles.instruction.data_providers_index') %> -

    -
    -

    <%= t('instruction.data_providers.title') %>

    -

    <%= t('.title') %>

    -
    -
    - -
    -

    <%= t('.subtitle') %>

    +

    <%= t('.title') %>

    - <% if @data_providers.empty? %> -

    <%= t('.empty') %>

    - <% else %> -
    - <% @data_providers.each do |data_provider| %> -
    - \ No newline at end of file diff --git a/app/views/instruction/formulaires/show.html.erb b/app/views/instruction/formulaires/show.html.erb index ff39b5b843..672eecf8c8 100644 --- a/app/views/instruction/formulaires/show.html.erb +++ b/app/views/instruction/formulaires/show.html.erb @@ -1,203 +1,110 @@ <% set_title! t('page_titles.instruction.formulaires_show', formulaire_name: @formulaire.name, provider_name: @data_provider.name) %> -<%= render 'instruction/data_providers/header' %> +<%= render 'instruction/formulaires/header' %> -
    -

    - <%= link_to t('.back'), - instruction_formulaires_path(provider_slug: @data_provider.slug), - class: 'fr-link fr-icon-arrow-left-line fr-link--icon-left' %> -

    +
    + +
    + <%= render 'instruction/shared/title_with_modify_button' do %> +

    Configuration

    + <% end %> -

    <%= @formulaire.name_with_stage %>

    -

    - <%= t('.sections.identification') %> : - <%= @formulaire.id %> -

    +
    +
    + <%= t('.fields.kind') %> : + <%= @formulaire.kind.presence || content_tag(:span, t('.empty_value'), class: 'fr-text-mention--grey') %> +
    - <% if @formulaire.description.present? %> -
    -

    <%= t('.sections.description') %>

    -

    <%= @formulaire.description %>

    -
    - <% end %> +
    + <%= t('.flags.startable_by_applicant') %> : + <%= t(".#{@formulaire.startable_by_applicant}") %> +
    -
    -

    <%= t('.sections.identity') %>

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    <%= t('.fields.label') %><%= t('.fields.value') %>
    <%= t('.fields.kind') %><%= @formulaire.kind.presence || content_tag(:span, t('.empty_value'), class: 'fr-text-mention--grey') %>
    <%= t('.fields.link') %> - <% if @formulaire.link.present? %> - <%= link_to @formulaire.link, @formulaire.link, class: 'fr-link', target: '_blank', rel: 'noopener' %> - <% else %> - <%= t('.empty_value') %> - <% end %> -
    <%= t('.fields.access_link') %> - <% if @formulaire.access_link.present? %> - <%= @formulaire.access_link %> - <% else %> - <%= t('.empty_value') %> - <% end %> -
    <%= t('.fields.cgu_link') %> - <% if @formulaire.cgu_link.present? %> - <%= link_to @formulaire.cgu_link, @formulaire.cgu_link, class: 'fr-link', target: '_blank', rel: 'noopener' %> - <% else %> - <%= t('.empty_value') %> - <% end %> -
    <%= t('.fields.support_email') %> - <% if @formulaire.support_email.present? %> - <%= mail_to @formulaire.support_email, @formulaire.support_email, class: 'fr-link' %> - <% else %> - <%= t('.empty_value') %> - <% end %> -
    -
    -
    +
    + <%= t('.flags.unique') %> : + <%= t(".#{@formulaire.unique}") %> +
    -
    -

    <%= t('.sections.flags') %>

    -
    - - - - - - - - - - - - - - - - - - - - - -
    <%= t('.fields.label') %><%= t('.fields.value') %>
    <%= t('.flags.public') %><%= t(".#{@formulaire.public}") %>
    <%= t('.flags.startable_by_applicant') %><%= t(".#{@formulaire.startable_by_applicant}") %>
    <%= t('.flags.unique') %><%= t(".#{@formulaire.unique}") %>
    -
    -
    + + <% t(".sections.features").each do |feature, name_and_default| %> + <% value = @formulaire.features[feature] || name_and_default[:default]%> +
    + <%= name_and_default[:name] %> : + <%= t(".#{value}", default: 'Non') %> +
    + <% end %> - <% if @formulaire.features.present? %> -
    -

    <%= t('.sections.features') %>

    -
      - <% @formulaire.features.each do |name, value| %> -
    • - <%= name %> : <%= t(".#{value}", default: value.to_s) %> -
    • - <% end %> -
    -
    - <% end %> -
    -

    <%= t('.sections.scopes') %>

    - <% if @formulaire.scopes.any? %> -
      - <% @formulaire.scopes.each do |scope| %> -
    • - <%= scope.name.presence || scope.value %> -
    • +
      + <%= t('.fields.access_link') %> : + <% if @formulaire.access_link.present? %> + <%= link_to @formulaire.access_link, @formulaire.access_link, class: 'fr-link' %> + <% else %> + <%= t('.empty_value') %> <% end %> -
    - <% else %> -

    <%= t('.empty_scopes') %>

    - <% end %> -
    +
    -
    -

    <%= t('.sections.blocks') %>

    - <% if @formulaire.blocks.any? %> -
      - <% @formulaire.blocks.each do |block| %> -
    1. <%= block[:name] || block['name'] %>
    2. +
      + <%= t('.fields.support_email') %> : + <% if @formulaire.support_email.present? %> + <%= mail_to @formulaire.support_email, @formulaire.support_email, class: 'fr-link' %> + <% else %> + <%= t('.empty_value') %> <% end %> -
    - <% else %> -

    <%= t('.empty_blocks') %>

    - <% end %> -
    +
    +
    +
    + + +
    +
    +
    -
    -

    <%= t('.sections.cas_usages') %>

    - <% if @cas_usages.any? %> -
    - <% @cas_usages.each do |cas_usage| %> - <% demandes_count = @demandes_counts.fetch(cas_usage.uid, 0) %> - <% habilitations_count = @habilitations_counts.fetch(cas_usage.uid, 0) %> -
    -
    -
    +
    +
    + + +<%= render 'instruction/shared/formulaire_wysiwyg', authorization_request: @authorization_request, with_cgus: true %> diff --git a/app/views/instruction/shared/_formulaire_wysiwyg.html.erb b/app/views/instruction/shared/_formulaire_wysiwyg.html.erb new file mode 100644 index 0000000000..027d6bbe5f --- /dev/null +++ b/app/views/instruction/shared/_formulaire_wysiwyg.html.erb @@ -0,0 +1,61 @@ +<% with_cgus ||= false %> + +
    +
    + <%= render 'instruction/shared/title_with_modify_button' do %> +

    Introduction du formulaire

    + <% end %> + +
    + <%= render 'authorization_request_forms/new', authorization_request_form: @cas_usage %> +
    +
    +
    + +<%= form_with model: authorization_request, url: '#', builder: AuthorizationRequestFormBuilder, html: { inert: '' } do |f| %> + <% static_block_names = @cas_usage.static_blocks.map { |b| b[:name] || b['name'] }.to_set %> + <% @formulaire.blocks.each_with_index do |block, index| %> + +
    + + <% if index == 0 %> +
    +

    Contenu du formulaire

    +
    + <% end %> + +
    + <%= render 'instruction/shared/title_with_modify_button' do %> +

    + <%= f.wording_for("steps.#{block[:name]}") %> + + + <% if static_block_names.include?(block[:name].to_s) %> + + Cette étape ne sera pas montrée à l'utilisateur + + <% end %> +

    + <% end %> + +
    + <%= render_custom_form_or_default(authorization_request, block[:name], { f: }) %> +
    +
    +
    + <% end %> + + <% if with_cgus %> +
    +
    + <%= render 'instruction/shared/title_with_modify_button' do %> +

    Avant de soumettre la demande

    + <% end %> + +
    + <%= render 'authorization_request_forms/shared/tos_checkboxes', f: f %> +
    +
    +
    + <% end %> +<% end %> diff --git a/app/views/instruction/shared/_stats_badges.html.erb b/app/views/instruction/shared/_stats_badges.html.erb new file mode 100644 index 0000000000..14f83f7f29 --- /dev/null +++ b/app/views/instruction/shared/_stats_badges.html.erb @@ -0,0 +1,12 @@ +
    + <%= habilitations_count %> + + <%= t('authorization_request.status.validated') %> + +
    +
    + <%= demandes_count %> + + <%= t('authorization_request.status.submitted') %> + +
    diff --git a/app/views/instruction/shared/_title_with_modify_button.html.erb b/app/views/instruction/shared/_title_with_modify_button.html.erb new file mode 100644 index 0000000000..371d98835f --- /dev/null +++ b/app/views/instruction/shared/_title_with_modify_button.html.erb @@ -0,0 +1,12 @@ +
    + +
    + +
    + +
    + <%= yield %> +
    +
    \ No newline at end of file diff --git a/app/views/layouts/wide_container.html.erb b/app/views/layouts/wide_container.html.erb new file mode 100644 index 0000000000..d05d93ae67 --- /dev/null +++ b/app/views/layouts/wide_container.html.erb @@ -0,0 +1,9 @@ +<%= content_for(:body) do %> +
    + <%= render partial: 'shared/alerts' %> + + <%= yield %> +
    +<% end %> + +<%= render template: 'layouts/application' %> diff --git a/config/locales/instruction_catalogue.fr.yml b/config/locales/instruction_catalogue.fr.yml index 01800ba6ae..72d5c07a5b 100644 --- a/config/locales/instruction_catalogue.fr.yml +++ b/config/locales/instruction_catalogue.fr.yml @@ -1,10 +1,8 @@ fr: instruction: data_providers: - title: Espace fournisseur de données index: title: Fournisseurs de données - subtitle: Sélectionnez un fournisseur pour consulter ses formulaires et cas d’usage. empty: Aucun fournisseur de données disponible. formulaires_count: zero: Aucun formulaire @@ -17,19 +15,34 @@ fr: cas_usages_count: one: 1 cas d’usage other: "%{count} cas d’usage" + emails_automatiques_count: + zero: Aucun email automatique + one: 1 email automatique + other: "%{count} emails automatiques" empty: Aucun formulaire associé à ce fournisseur. show: - title: Formulaire - back: Retour aux formulaires + identification: Identifiant du formulaire sections: - identification: Identification identity: Identité description: Description scopes: Périmètres de données blocks: Structure du formulaire - cas_usages: Cas d’usage flags: Propriétés - features: Fonctionnalités + features: + instructor_drafts: + name: "Initiable par un instructeur" + default: false + messaging: + name: "Messagerie via datapass active" + default: true + reopening: + name: "Demande de modification possible" + default: true + transfer: + name: "Transfert de demandeur possible" + default: true + + fields: label: Champ value: Valeur @@ -41,29 +54,21 @@ fr: flags: public: Public startable_by_applicant: Démarrable par le demandeur - unique: Unique + unique: Habilitation unique par orgnanisation empty_value: Non renseigné empty_scopes: Aucun périmètre déclaré. empty_blocks: Aucun bloc déclaré. empty_cas_usages: Aucun cas d’usage associé à ce formulaire. - default_badge: Par défaut "true": Oui "false": Non - stats: - demandes: - zero: Aucune demande - one: 1 demande - other: "%{count} demandes" - habilitations: - zero: Aucune habilitation - one: 1 habilitation - other: "%{count} habilitations" cas_usages: + index: + title: Cas d'usages + default_badge: Par défaut show: - back: Retour au formulaire + identification: Identifiant du cas d’usage inherited_badge: Hérité du formulaire sections: - identification: Identification description: Description introduction: Introduction flags: Propriétés @@ -74,9 +79,9 @@ fr: flags: public: Public startable_by_applicant: Démarrable par le demandeur - single_page_view: Template single-page + single_page_view: Formulaire en une seule page single_page_view_hint: "Nom optionnel du template ERB utilisé quand le formulaire est en mode single-page (steps vide). Sinon, fallback sur l’uid du cas d’usage." - single_page_view_default: "(template par défaut)" + single_page_view_default: "Non" empty_description: Aucune description. empty_introduction: Aucune introduction. html_tabs: @@ -117,13 +122,32 @@ fr: form: Rendu data: Données form_intro: "Aperçu du formulaire pré-rempli pour le demandeur (champs en lecture seule) :" - no_service_provider: Pas de service utilisateur rattaché. - default_badge: Par défaut + no_service_provider: Pas de fournisseur de service rattaché. "true": Oui "false": Non + emails_automatiques: + show: + title: Emails automatiques + no_preview: Aucun aperçu disponible (aucune demande de ce type en base). + events: + submit: Soumission d'une demande + approve: Validation d'une demande + refuse: Refus d'une demande + request_changes: Demande de modifications + revoke: Révocation d'une habilitation + recipients: + applicant: Demandeur + instructors: Instructeurs + responsable_traitement: Responsable de traitement + delegue_protection_donnees: Délégué à la protection des données + france_connect: Support FranceConnect + dgfip_apim: DGFIP APIM + hubee_administrateur_metier: Administrateur métier HubEE + from_credentials: Emails configurés dans les credentials pour %{definition_name} page_titles: instruction: data_providers_index: Fournisseurs de données formulaires_index: "%{provider_name} — Formulaires" formulaires_show: "%{formulaire_name} — %{provider_name}" cas_usages_show: "%{cas_usage_name} — %{provider_name}" + emails_automatiques_show: "Emails automatiques — %{formulaire_name}" diff --git a/config/routes.rb b/config/routes.rb index 31bf3d18f2..e2a7a35152 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -154,7 +154,8 @@ get '/fournisseurs-donnees', to: 'data_providers#index', as: :data_providers scope '/fournisseurs-donnees/:provider_slug' do resources :formulaires, only: %i[index show] do - resources :cas_usages, only: :show, path: 'cas-usage', param: :uid + resources :cas_usages, only: %i[index show], path: 'cas-usage', param: :uid + resource :emails_automatiques, only: :show, path: 'emails-automatiques' end end end diff --git a/features/instructeurs/emails_automatiques.feature b/features/instructeurs/emails_automatiques.feature new file mode 100644 index 0000000000..24b73be02f --- /dev/null +++ b/features/instructeurs/emails_automatiques.feature @@ -0,0 +1,25 @@ +# language: fr + +Fonctionnalité: Emails automatiques d'un formulaire + Un instructeur peut consulter la liste des emails automatiques + envoyés par DataPass pour un formulaire donné. + + @CreateDataProviders + Scénario: Je consulte les emails automatiques depuis la page formulaire + Sachant que je suis un rapporteur "API Entreprise" + Et que je me connecte + Quand je me rends sur le chemin "/instruction/fournisseurs-donnees/dinum/formulaires/api_entreprise" + Alors la page contient "Emails automatiques" + Quand je clique sur "Emails automatiques" + Alors le titre de la page contient "Emails automatiques" + Et la page contient "Soumission d'une demande" + Et la page contient "Demandeur" + + @CreateDataProviders + Scénario: La page des emails automatiques liste les emails avec leur sujet + Sachant que je suis un rapporteur "API Entreprise" + Et que je me connecte + Quand je me rends sur le chemin "/instruction/fournisseurs-donnees/dinum/formulaires/api_entreprise/emails-automatiques" + Alors le titre de la page contient "Emails automatiques" + Et la page contient "Nous accusons réception" + Et la page contient "Instructeurs" diff --git a/spec/services/automatic_emails_catalog_spec.rb b/spec/services/automatic_emails_catalog_spec.rb new file mode 100644 index 0000000000..49772f332a --- /dev/null +++ b/spec/services/automatic_emails_catalog_spec.rb @@ -0,0 +1,65 @@ +RSpec.describe AutomaticEmailsCatalog do + subject(:catalog) { described_class.new(definition) } + + let(:definition) { AuthorizationDefinition.find('api_entreprise') } + + describe '#build' do + subject(:emails) { catalog.build } + + it 'returns AutomaticEmailEntry objects' do + expect(emails).to all(be_a(described_class::AutomaticEmailEntry)) + end + + context 'with APIEntrepriseNotifier (via APIEntreculierNotifier)' do + it 'includes the submit email to the applicant' do + expect(emails).to include(have_attributes(event_name: 'submit', recipient_type: :applicant)) + end + + it 'includes the submit email to instructors' do + expect(emails).to include(have_attributes(event_name: 'submit', recipient_type: :instructors)) + end + + it 'does not include an approve email to the applicant' do + expect(emails).not_to include(have_attributes(event_name: 'approve', recipient_type: :applicant)) + end + + it 'does not include a refuse email' do + expect(emails).not_to include(have_attributes(event_name: 'refuse')) + end + end + + context 'with a definition using BaseNotifier with GDPR contacts' do + let(:definition) { AuthorizationDefinition.find('api_scolarite') } + + it 'includes the approve email to the applicant' do + expect(emails).to include(have_attributes(event_name: 'approve', recipient_type: :applicant)) + end + + it 'includes the refuse email to the applicant' do + expect(emails).to include(have_attributes(event_name: 'refuse', recipient_type: :applicant)) + end + + it 'includes the GDPR responsable traitement email on approve' do + expect(emails).to include(have_attributes(event_name: 'approve', recipient_type: :responsable_traitement)) + end + + it 'includes the GDPR delegue protection donnees email on approve' do + expect(emails).to include(have_attributes(event_name: 'approve', recipient_type: :delegue_protection_donnees)) + end + end + + context 'with a DGFIP definition' do + let(:definition) { AuthorizationDefinition.find('api_impot_particulier') } + + before { create(:data_provider, :dgfip) } + + it 'includes the DGFIP APIM email on approve' do + expect(emails).to include(have_attributes(event_name: 'approve', recipient_type: :dgfip_apim)) + end + end + + it 'includes a non-empty subject for each email' do + expect(emails.map(&:subject)).to all(be_present) + end + end +end