Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion app/controllers/buyer/public_markets_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class PublicMarketsController < ApplicationController

def show
case step
when :setup, :summary, :lot_config
when :setup, :summary, :lot_config, :form_config
render_wizard
else
@current_category = step.to_s
Expand Down Expand Up @@ -66,6 +66,8 @@ def step_params
lot_limit_enabled: params[:lot_limit_enabled],
lot_limit: params[:lot_limit]
}
when :form_config
{}
else
# Category step - collect selected optional fields
{ selected_attribute_keys: params[:selected_attribute_keys] || [] }
Expand Down
17 changes: 16 additions & 1 deletion app/presenters/public_market_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class PublicMarketPresenter

INITIAL_WIZARD_STEP = :setup
LOT_CONFIG_STEP = :lot_config
FORM_CONFIG_STEP = :form_config
FINAL_WIZARD_STEP = :summary

def initialize(public_market)
Expand All @@ -14,7 +15,10 @@ def initialize(public_market)

def wizard_steps
steps = [INITIAL_WIZARD_STEP]
steps << LOT_CONFIG_STEP if @public_market.lots.any?
if @public_market.lots.any?
steps << LOT_CONFIG_STEP
steps << FORM_CONFIG_STEP
end
Comment on lines +18 to +21
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je me demande si on ne peut pas faire un truc un peu plus smart. C'est pas du tgout un sujet related à cette PR mais j'ai quand même le sentiment qu'on empile des exceptions et qu'en fait il y a une norme qui se dessine que l'on devrait exploiter pour refacto le bouzin.

steps + available_category_keys.map(&:to_sym) + [FINAL_WIZARD_STEP]
end

Expand Down Expand Up @@ -100,6 +104,17 @@ def market_types_label_with_source
"#{market_types_label} (#{I18n.t('market_types.source.platform')})"
end

def lots_by_effective_type
lots_for_config.group_by(&:effective_market_type)
end

def lot_effective_type_label(lot)
type = lot.effective_market_type
return nil unless type

I18n.t("market_types.#{type.code}", default: type.code.humanize)
end

def lots
@lots ||= @public_market.lots.ordered.to_a
end
Expand Down
2 changes: 2 additions & 0 deletions app/services/buyer/public_market_wizard_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def call
handle_setup_step
when :lot_config
handle_lot_config_step
when :form_config
public_market
when :summary
complete_market
else
Expand Down
80 changes: 80 additions & 0 deletions app/views/buyer/public_markets/form_config.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<% content_for :title, "#{@public_market.editor.name} - #{t('buyer.public_markets.form_config.title')}" %>

<h1 class="fr-h1 fr-mb-1w"><%= t('buyer.public_markets.form_config.title') %></h1>
<p class="fr-text--lg fr-mb-4w"><%= t('buyer.public_markets.form_config.subtitle') %></p>

<%= render 'shared/notice', notice_text: t('buyer.public_markets.form_config.banner') %>

<div class="fr-grid-row fr-grid-row--gutters">
<div class="fr-col-12 fr-col-md-4">
<div class="fr-mb-3w fr-background-alt--grey" style="padding: 20px;">
<h2 class="fr-text--md fr-text--bold" style="margin-bottom: 0.5rem;">
<%= t('buyer.shared.market_info_title') %>
</h2>
<hr style="border: none; border-top: 1px solid var(--border-default-grey); margin: 0;">
<%= render 'shared/sidebar_market_info',
buyer_label: t('buyer.public_markets.form_config.sidebar_buyer_label'),
market_name: @public_market.buyer_display_name,
typology_label: t('buyer.public_markets.form_config.sidebar_typology_label'),
market_types_label: @public_market.market_type_codes.any? ? @presenter.market_types_label : nil,
deadline_label: t('buyer.shared.deadline_short'),
deadline: l(@public_market.deadline, format: '%d/%m/%Y %H:%M') %>
</div>

<div class="fr-background-alt--grey" style="padding: 20px;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.5rem;">
<h2 class="fr-text--md fr-text--bold" style="margin: 0;">
<%= t('buyer.public_markets.form_config.lots_section_title') %>
</h2>
<%= link_to step_buyer_public_market_path(identifier: @public_market.identifier, id: :lot_config),
class: 'fr-btn fr-btn--tertiary-no-outline fr-btn--sm fr-icon-edit-line',
title: t('buyer.public_markets.form_config.edit_lots_label'),
aria: { label: t('buyer.public_markets.form_config.edit_lots_label') } do %>
<% end %>
</div>
<hr style="border: none; border-top: 1px solid var(--border-default-grey); margin: 0 0 0.75rem 0;">
<div style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
<% @presenter.lots_for_config.each do |lot| %>
<span class="fr-tag fr-tag--sm">
<% type_label = @presenter.lot_effective_type_label(lot) %>
<%= type_label ? "#{lot.name} - #{type_label.downcase}" : lot.name %>
</span>
<% end %>
</div>
</div>
</div>

<div class="fr-col-12 fr-col-md-8">
<div class="fr-background-alt--grey fr-p-4w fr-mb-3w">
<div class="fr-mb-2w" style="display: flex; gap: 0.5rem; flex-wrap: wrap;">
<% @presenter.lots_by_effective_type.each do |market_type, type_lots| %>
<% if market_type %>
<span class="fr-tag fr-tag--sm">
<%= t("market_types.#{market_type.code}", default: market_type.code.humanize) %>
</span>
<% end %>
<span class="fr-tag fr-tag--sm">
<%= t('buyer.public_markets.form_config.lots_count', count: type_lots.size) %>
</span>
<% end %>
</div>

<h2 class="fr-h5 fr-mb-3w"><%= t('buyer.public_markets.form_config.form_block_title') %></h2>

<%= form_with url: wizard_path, method: :put, local: true, data: { turbo: false } do |_form| %>
<div style="display: flex; justify-content: flex-end;">
<button type="submit" class="fr-btn fr-icon-arrow-right-line fr-btn--icon-right">
<%= t('buyer.public_markets.form_config.configure_button') %>
</button>
</div>
<% end %>
</div>

<button type="button"
class="fr-btn fr-btn--secondary fr-icon-arrow-right-line fr-btn--icon-right"
style="width: 100%; justify-content: center;"
disabled>
<%= t('buyer.public_markets.form_config.transmit_button') %>
</button>
</div>
</div>
69 changes: 39 additions & 30 deletions app/views/buyer/public_markets/lot_config.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
<% content_for :title, "#{@public_market.editor.name} - #{t('buyer.public_markets.lot_config.title')}" %>

<div class="fr-grid-row fr-grid-row--gutters">
<div class="fr-col-12 fr-col-md-7">
<div class="fr-col-12 fr-col-md-4">
<div class="fr-mb-3w fr-background-alt--grey" style="padding: 20px;">
<h2 class="fr-text--md fr-text--bold" style="margin-bottom: 0.5rem;">
<%= t('buyer.shared.market_info_title') %>
</h2>
<hr style="border: none; border-top: 1px solid var(--border-default-grey); margin: 0;">
<%= render 'shared/sidebar_market_info',
buyer_label: t('buyer.public_markets.lot_config.sidebar_buyer_label'),
market_name: @public_market.buyer_display_name,
typology_label: t('buyer.public_markets.lot_config.sidebar_typology_label'),
market_types_label: @public_market.market_type_codes.any? ? @presenter.market_types_label : nil,
deadline_label: t('buyer.shared.deadline_short'),
deadline: l(@public_market.deadline, format: '%d/%m/%Y %H:%M') %>
</div>

<div class="fr-background-alt--grey" style="padding: 20px;">
<h2 class="fr-text--md fr-text--bold" style="margin-bottom: 0.5rem;">
<%= t('buyer.public_markets.lot_config.how_it_works_title') %>
</h2>
<hr style="border: none; border-top: 1px solid var(--border-default-grey); margin: 0;">
<ol style="margin: 0; padding-left: 1.5rem; font-size: 0.875rem; display: flex; flex-direction: column; gap: 4px;">
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_1') %></li>
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_2') %></li>
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_3') %></li>
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_4') %></li>
</ol>
</div>
</div>

<div class="fr-col-12 fr-col-md-8">
<h1 class="fr-h1 fr-mb-1w"><%= t('buyer.public_markets.lot_config.title') %></h1>
<p class="fr-text--lg fr-mb-4w"><%= t('buyer.public_markets.lot_config.subtitle') %></p>

Expand All @@ -25,13 +54,22 @@
<table>
<thead>
<tr>
<th scope="col" style="width: 3rem;"></th>
<th scope="col"><%= t('buyer.shared.lot_name') %></th>
<th scope="col"><%= t('buyer.shared.market_type_short') %></th>
</tr>
</thead>
<tbody>
<% @presenter.lots_for_config.each do |lot| %>
<tr>
<td style="width: 3rem; text-align: center; vertical-align: middle;">
<%= label_tag "lot_#{lot.id}", class: 'fr-sr-only' do %>
<%= t('buyer.public_markets.lot_config.select_lot', name: lot.name) %>
<% end %>
<%= check_box_tag 'selected_lot_ids[]', lot.id, false,
id: "lot_#{lot.id}",
style: 'width: 1.125rem; height: 1.125rem; cursor: pointer; accent-color: var(--blue-france-sun-113-625);' %>
</td>
<td><%= lot.name %></td>
<td><%= render 'buyer/public_markets/lot_type_badge', lot: lot %></td>
</tr>
Expand Down Expand Up @@ -102,33 +140,4 @@
</button>
<% end %>
</div>

<div class="fr-col-12 fr-col-md-4 fr-col-offset-md-1">
<div class="fr-mb-3w fr-background-alt--grey" style="padding: 20px;">
<h2 class="fr-text--md fr-text--bold" style="margin-bottom: 0.5rem;">
<%= t('buyer.shared.market_info_title') %>
</h2>
<hr style="border: none; border-top: 1px solid var(--border-default-grey); margin: 0;">
<%= render 'shared/sidebar_market_info',
buyer_label: t('buyer.public_markets.lot_config.sidebar_buyer_label'),
market_name: @public_market.name,
typology_label: t('buyer.public_markets.lot_config.sidebar_typology_label'),
market_types_label: @public_market.market_type_codes.any? ? @presenter.market_types_label : nil,
deadline_label: t('buyer.shared.deadline_short'),
deadline: l(@public_market.deadline, format: '%d/%m/%Y %H:%M') %>
</div>

<div class="fr-background-alt--grey" style="padding: 20px;">
<h2 class="fr-text--md fr-text--bold" style="margin-bottom: 0.5rem;">
<%= t('buyer.public_markets.lot_config.how_it_works_title') %>
</h2>
<hr style="border: none; border-top: 1px solid var(--border-default-grey); margin: 0;">
<ol style="margin: 0; padding-left: 1.5rem; font-size: 0.875rem; display: flex; flex-direction: column; gap: 4px;">
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_1') %></li>
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_2') %></li>
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_3') %></li>
<li><%= t('buyer.public_markets.lot_config.how_it_works_step_4') %></li>
</ol>
</div>
</div>
</div>
25 changes: 20 additions & 5 deletions config/locales/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ fr:
title_suffix: "Synthèse de ma candidature"
market_info_title: "Informations du marché"
no_fields_configured: "Aucun champ configuré."
finalize: "Finaliser la configuration"
finalize: "Transmettre la configuration"
lot_limit: "Limite de lots par candidat"
lot_limit_value:
one: "%{count} lot maximum"
Expand Down Expand Up @@ -403,13 +403,13 @@ fr:
capacites_techniques_professionnelles: Sélections des renseignements complémentaires (partie 2)
summary: "Synthèse"
lot_config:
title: "Configurer le type de vos lots"
title: "Configurez le type de vos lots"
subtitle: "Vérifiez et ajustez le type de chaque lot avant de configurer les pièces justificatives."
lots_section_title: "Vérifiez les types transmis"
lots_section_subtitle: "Assurez-vous que les types transmis par la plateforme de marché sont exacts et modifiez-les si besoin."
platform_banner_global_type: "La plateforme %{platform} a transmis le type %{type} pour l'ensemble des lots. Sélectionnez les lots dont le type est incorrect pour les modifier."
platform_banner_per_lot_type: "La plateforme %{platform} a transmis un type distinct pour chaque lot. Sélectionnez les lots dont le type est incorrect pour les modifier."
platform_source: "Plateforme"
platform_banner_global_type: "Votre plateforme d'achat nous a transmis une typologie de marché pour les lots, si le type d'un lot est erroné vous pouvez le sélectionner pour lui appliquer la bonne typologie (travaux, services, fournitures)"
platform_banner_per_lot_type: "Votre plateforme d'achat nous a transmis une typologie de marché pour les lots, si le type d'un lot est erroné vous pouvez le sélectionner pour lui appliquer la bonne typologie (travaux, services, fournitures)"
platform_source: "PLATEFORME"
lot_number: "Lot %{number}"
lot_limit_section_title: "Indiquez si vous souhaitez limiter le nombre de lots"
lot_limit_section_subtitle: "Si vous le souhaitez, vous pouvez ajuster le nombre de lots auxquels une entreprise peut candidater."
Expand All @@ -424,7 +424,22 @@ fr:
how_it_works_step_2: "Modifiez les types de lots si nécessaires"
how_it_works_step_3: "Limitez le nombre de lots si nécessaire"
how_it_works_step_4: "Passez à la configuration des candidatures"
select_lot: "Sélectionner le lot %{name}"
submit: "Configurer les candidatures"
form_config:
title: "Configurez le formulaire de candidature"
subtitle: "Configurez les pièces justificatives attendues dans le cadre de la candidature à votre marché."
banner: "Vous n'avez qu'un seul formulaire de candidature à configurer, Passe Marché se charge de transmettre votre configuration pour les différents lots."
form_block_title: "Formulaire de candidature à configurer"
configure_button: "Configurer"
transmit_button: "Transmettre la configuration"
lots_section_title: "Liste des lots"
lots_count:
one: "1 lot"
other: "%{count} lots"
edit_lots_label: "Modifier la sélection des lots"
sidebar_buyer_label: "Acheteur"
sidebar_typology_label: "Typologie"
generic_step:
optional_question: Souhaitez-vous demander des renseignements complémentaires ?
yes_option: Oui
Expand Down
4 changes: 2 additions & 2 deletions features/buyer_flow.feature
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ Feature: Buyer Configuration Flow
And I should see "Lot 1 - Ordinateurs portables"

When I click on "Débuter l'activation de"
Then I should see "Configurer le type de vos lots"
Then I should see "Configurez le type de vos lots"

When I navigate through all category steps to summary
Then I should be on the summary page
And I should see "Synthèse des paramètres de la candidature"
And I should see "Informations du marché"
And I should see a button "Finaliser la configuration"
And I should see a button "Transmettre la configuration"

Scenario: Navigation arrière avec les boutons Précédent
Given I am on the summary page for my public market
Expand Down
22 changes: 20 additions & 2 deletions features/buyer_lot_config.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Feature: Buyer Lot Configuration Step

Scenario: L'acheteur accède à la page de configuration des lots
When I visit the lot_config page for my public market
Then I should see "Configurer le type de vos lots"
Then I should see "Configurez le type de vos lots"
And I should see "Lot 1"
And I should see "Lot 2"
And I should see "Lot 3"
Expand All @@ -22,7 +22,7 @@ Feature: Buyer Lot Configuration Step
When I visit the setup page for my public market
And I click on "Débuter l'activation de"
Then I should be on the lot_config page
And I should see "Configurer le type de vos lots"
And I should see "Configurez le type de vos lots"

Scenario: L'acheteur choisit de ne pas limiter les lots
When I visit the lot_config page for my public market
Expand Down Expand Up @@ -61,4 +61,22 @@ Feature: Buyer Lot Configuration Step
Then I should be on the lot_config page
When I choose "Non" for lot limit
And I submit the lot_config form
Then I should be on the form_config page
And I should see "Configurez le formulaire de candidature"

Scenario: La page form_config affiche la liste des lots et le bouton Configurer
When I visit the form_config page for my public market
Then I should see "Configurez le formulaire de candidature"
And I should see "Formulaire de candidature à configurer"
And I should see "Configurer"
And I should see "Transmettre la configuration"

Scenario: Le bouton crayon sur form_config renvoie vers lot_config
When I visit the form_config page for my public market
And I click the edit lots button
Then I should be on the lot_config page

Scenario: Depuis form_config, Configurer mène à la première étape de catégorie
When I visit the form_config page for my public market
And I click on "Configurer"
Then I should be on the first category page
11 changes: 8 additions & 3 deletions features/step_definitions/buyer_flow_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
@market_identifier ||= @last_api_response['identifier']
public_market = PublicMarket.find_by!(identifier: @market_identifier)
presenter = PublicMarketPresenter.new(public_market)
special_steps = %i[setup lot_config summary]
special_steps = %i[setup lot_config form_config summary]
@first_category = presenter.wizard_steps.find { |s| special_steps.exclude?(s) }
visit step_buyer_public_market_path(identifier: @market_identifier, id: @first_category)
end
Expand All @@ -34,7 +34,7 @@
visit step_buyer_public_market_path(identifier: @market_identifier, id: :summary)
end

SPECIAL_WIZARD_STEPS = %i[setup lot_config summary].freeze
SPECIAL_WIZARD_STEPS = %i[setup lot_config form_config summary].freeze

When('I navigate through all category steps to summary') do
public_market = PublicMarket.find_by!(identifier: @market_identifier)
Expand All @@ -46,6 +46,11 @@
find('button[type="submit"]').click
end

if presenter.wizard_steps.include?(:form_config) &&
page.current_path == step_buyer_public_market_path(@market_identifier, :form_config)
find('button[type="submit"]').click
end

category_steps = presenter.wizard_steps.reject { |s| SPECIAL_WIZARD_STEPS.include?(s) }

category_steps.each do
Expand All @@ -66,7 +71,7 @@
Then('I should be on the first category page') do
public_market = PublicMarket.find_by!(identifier: @market_identifier)
presenter = PublicMarketPresenter.new(public_market)
special_steps = %i[setup lot_config summary]
special_steps = %i[setup lot_config form_config summary]
first_category = presenter.wizard_steps.find { |s| special_steps.exclude?(s) }
expect(page).to have_current_path(step_buyer_public_market_path(@market_identifier, first_category))
end
Expand Down
Loading