Skip to content

Commit a169756

Browse files
committed
component: ChampPrefilledBadgeComponent + view integration + spec
Add badge visible by instructeur/expert on prefilled champs: - "Donnee verifiee du referentiel" (success) when unchanged - "Donnee du referentiel modifiee par l'usager" (warning) when modified Not shown to usagers or on old dossiers without prefilled_original_value. tests: system spec instructeur badge prefilled + fix badge placement Move ChampPrefilledBadgeComponent render outside RowShowComponent block. RowShowComponent only renders named slots (value, blank) — content in the block outside slots is silently ignored.
1 parent b406be5 commit a169756

8 files changed

Lines changed: 215 additions & 60 deletions

File tree

app/assets/stylesheets/demande.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929
ul {
3030
margin-block-start: 0;
3131
}
32+
33+
.copy-zone > p:last-child {
34+
margin-bottom: 0;
35+
}
3236
}
3337

3438
// Pour un dossier lié, seuls les noms (démarche, organisme) sont en gras (balises strong).
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
class Dossiers::ChampPrefilledBadgeComponent < ApplicationComponent
4+
def initialize(champ:, profile:)
5+
@champ = champ
6+
@profile = profile
7+
end
8+
9+
def render?
10+
@profile != 'usager' && @champ.prefilled? && @champ.prefilled_original_value.present?
11+
end
12+
13+
def tooltip_id
14+
"tooltip-prefilled-#{@champ.id}"
15+
end
16+
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
en:
3+
modified: "Referentiel data modified by the user"
4+
verified: "Verified referentiel data"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fr:
3+
modified: "Donnée du référentiel modifiée par l’usager"
4+
verified: "Donnée vérifiée du référentiel"
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<% if @champ.prefilled_value_modified? %>
2+
<p class="fr-message fr-message--warning" style="flex-basis: 100%; margin: 0">
3+
<%= t('.modified') %>
4+
</p>
5+
<% else %>
6+
<span
7+
class="fr-icon-checkbox-circle-line fr-icon--sm fr-text-default--info"
8+
aria-describedby="<%= tooltip_id %>"
9+
></span>
10+
<span
11+
class="fr-tooltip fr-placement"
12+
role="tooltip"
13+
aria-hidden="true"
14+
id="<%= tooltip_id %>"
15+
>
16+
<%= t('.verified') %>
17+
</span>
18+
<% end %>

app/components/dossiers/champs_rows_show_component/champs_rows_show_component.html.erb

Lines changed: 64 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,70 +15,74 @@
1515
<% end %>
1616
<% else %>
1717
<% c.with_value do %>
18-
<% case champ.type_champ %>
19-
<% when TypeDeChamp.type_champs.fetch(:carte) %>
20-
<%= render partial: "shared/champs/carte/show", locals: { champ: champ } %>
21-
<% when TypeDeChamp.type_champs.fetch(:dossier_link) %>
22-
<%= render partial: "shared/champs/dossier_link/show", locals: { champ: champ } %>
23-
<% when TypeDeChamp.type_champs.fetch(:drop_down_list) %>
24-
<%= render partial: "shared/champs/drop_down_list/show", locals: { champ: champ } %>
25-
<% when TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) %>
26-
<%= render partial: "shared/champs/multiple_drop_down_list/show", locals: { champ: champ } %>
27-
<% when TypeDeChamp.type_champs.fetch(:piece_justificative) %>
28-
<%= render partial: "shared/champs/piece_justificative/show", locals: { champ: champ, profile: @profile } %>
29-
<% when TypeDeChamp.type_champs.fetch(:siret) %>
30-
<% etablissement = champ.etablissement %>
31-
<% if etablissement.present? %>
32-
<% if etablissement.as_degraded_mode? %>
33-
<%= render Dossiers::DegradedIdentiteEntrepriseComponent.new(etablissement:, profile: @profile) %>
34-
<% elsif @profile == 'usager' %>
35-
<%= render Dossiers::IdentiteEntrepriseForUsagerComponent.new(etablissement:) %>
18+
<div class="flex align-baseline wrap" style="column-gap: 0.5rem">
19+
<% case champ.type_champ %>
20+
<% when TypeDeChamp.type_champs.fetch(:carte) %>
21+
<%= render partial: "shared/champs/carte/show", locals: { champ: champ } %>
22+
<% when TypeDeChamp.type_champs.fetch(:dossier_link) %>
23+
<%= render partial: "shared/champs/dossier_link/show", locals: { champ: champ } %>
24+
<% when TypeDeChamp.type_champs.fetch(:drop_down_list) %>
25+
<%= render partial: "shared/champs/drop_down_list/show", locals: { champ: champ } %>
26+
<% when TypeDeChamp.type_champs.fetch(:multiple_drop_down_list) %>
27+
<%= render partial: "shared/champs/multiple_drop_down_list/show", locals: { champ: champ } %>
28+
<% when TypeDeChamp.type_champs.fetch(:piece_justificative) %>
29+
<%= render partial: "shared/champs/piece_justificative/show", locals: { champ: champ, profile: @profile } %>
30+
<% when TypeDeChamp.type_champs.fetch(:siret) %>
31+
<% etablissement = champ.etablissement %>
32+
<% if etablissement.present? %>
33+
<% if etablissement.as_degraded_mode? %>
34+
<%= render Dossiers::DegradedIdentiteEntrepriseComponent.new(etablissement:, profile: @profile) %>
35+
<% elsif @profile == 'usager' %>
36+
<%= render Dossiers::IdentiteEntrepriseForUsagerComponent.new(etablissement:) %>
37+
<% else %>
38+
<%= render Dossiers::IdentiteEntrepriseComponent.new(champ:, avis: @avis) %>
39+
<% end %>
40+
<% end %>
41+
<% when TypeDeChamp.type_champs.fetch(:iban) %>
42+
<%= render partial: "shared/champs/iban/show", locals: { champ: champ } %>
43+
<% when TypeDeChamp.type_champs.fetch(:annuaire_education) %>
44+
<%= render Dossiers::AnnuaireEducationComponent.new(champ:) %>
45+
<% when TypeDeChamp.type_champs.fetch(:address) %>
46+
<%= render Dossiers::AddressComponent.new(champ:) %>
47+
<% when TypeDeChamp.type_champs.fetch(:communes) %>
48+
<%= render Dossiers::CommuneComponent.new(champ:) %>
49+
<% when TypeDeChamp.type_champs.fetch(:departements) %>
50+
<%= render Dossiers::DepartementComponent.new(champ:) %>
51+
<% when TypeDeChamp.type_champs.fetch(:regions) %>
52+
<%= render Dossiers::RegionComponent.new(champ:) %>
53+
<% when TypeDeChamp.type_champs.fetch(:rna) %>
54+
<%= render Dossiers::RNAComponent.new(champ:) %>
55+
<% when TypeDeChamp.type_champs.fetch(:rnf) %>
56+
<%= render Dossiers::RNFComponent.new(champ:) %>
57+
<% when TypeDeChamp.type_champs.fetch(:epci) %>
58+
<%= render Dossiers::EpciComponent.new(champ:) %>
59+
<% when TypeDeChamp.type_champs.fetch(:cojo) %>
60+
<%= render partial: "shared/champs/cojo/show", locals: { champ: champ, profile: @profile } %>
61+
<% when TypeDeChamp.type_champs.fetch(:date) %>
62+
<% if @profile != 'usager' && champ.prefilled_from_france_connect_information? %>
63+
<%= render Dossiers::DatePrefilledFromFranceConnectComponent.new(champ: champ) %>
3664
<% else %>
37-
<%= render Dossiers::IdentiteEntrepriseComponent.new(champ:, avis: @avis) %>
65+
<p class="copy-zone"><%= champ.to_s %></p>
3866
<% end %>
39-
<% end %>
40-
<% when TypeDeChamp.type_champs.fetch(:iban) %>
41-
<%= render partial: "shared/champs/iban/show", locals: { champ: champ } %>
42-
<% when TypeDeChamp.type_champs.fetch(:annuaire_education) %>
43-
<%= render Dossiers::AnnuaireEducationComponent.new(champ:) %>
44-
<% when TypeDeChamp.type_champs.fetch(:address) %>
45-
<%= render Dossiers::AddressComponent.new(champ:) %>
46-
<% when TypeDeChamp.type_champs.fetch(:communes) %>
47-
<%= render Dossiers::CommuneComponent.new(champ:) %>
48-
<% when TypeDeChamp.type_champs.fetch(:departements) %>
49-
<%= render Dossiers::DepartementComponent.new(champ:) %>
50-
<% when TypeDeChamp.type_champs.fetch(:regions) %>
51-
<%= render Dossiers::RegionComponent.new(champ:) %>
52-
<% when TypeDeChamp.type_champs.fetch(:rna) %>
53-
<%= render Dossiers::RNAComponent.new(champ:) %>
54-
<% when TypeDeChamp.type_champs.fetch(:rnf) %>
55-
<%= render Dossiers::RNFComponent.new(champ:) %>
56-
<% when TypeDeChamp.type_champs.fetch(:epci) %>
57-
<%= render Dossiers::EpciComponent.new(champ:) %>
58-
<% when TypeDeChamp.type_champs.fetch(:cojo) %>
59-
<%= render partial: "shared/champs/cojo/show", locals: { champ: champ, profile: @profile } %>
60-
<% when TypeDeChamp.type_champs.fetch(:date) %>
61-
<% if @profile != 'usager' && champ.prefilled_from_france_connect_information? %>
62-
<%= render Dossiers::DatePrefilledFromFranceConnectComponent.new(champ: champ) %>
63-
<% else %>
67+
<% when TypeDeChamp.type_champs.fetch(:datetime) %>
6468
<p class="copy-zone"><%= champ.to_s %></p>
69+
<% when TypeDeChamp.type_champs.fetch(:number), TypeDeChamp.type_champs.fetch(:integer_number), TypeDeChamp.type_champs.fetch(:decimal_number) %>
70+
<p class="copy-zone">
71+
<%= helpers.number_with_html_delimiter(champ.to_s) %>
72+
<span data-copy-message-placeholder></span>
73+
</p>
74+
<% when TypeDeChamp.type_champs.fetch(:referentiel) %>
75+
<%= render Dossiers::ReferentielComponent.new(champ:, profile: @profile) %>
76+
<% when TypeDeChamp.type_champs.fetch(:quotient_familial) %>
77+
<%= render Dossiers::QuotientFamilialComponent.new(champ:, profile: @profile) %>
78+
<% else %>
79+
<div class="copy-zone" data-to-copy="<%= champ.to_s.strip %>">
80+
<%= helpers.format_text_value(champ.to_s.strip) %>
81+
</div>
6582
<% end %>
66-
<% when TypeDeChamp.type_champs.fetch(:datetime) %>
67-
<p class="copy-zone"><%= champ.to_s %></p>
68-
<% when TypeDeChamp.type_champs.fetch(:number), TypeDeChamp.type_champs.fetch(:integer_number), TypeDeChamp.type_champs.fetch(:decimal_number) %>
69-
<p class="copy-zone">
70-
<%= helpers.number_with_html_delimiter(champ.to_s) %>
71-
<span data-copy-message-placeholder></span>
72-
</p>
73-
<% when TypeDeChamp.type_champs.fetch(:referentiel) %>
74-
<%= render Dossiers::ReferentielComponent.new(champ:, profile: @profile) %>
75-
<% when TypeDeChamp.type_champs.fetch(:quotient_familial) %>
76-
<%= render Dossiers::QuotientFamilialComponent.new(champ:, profile: @profile) %>
77-
<% else %>
78-
<div class="copy-zone" data-to-copy="<%= champ.to_s.strip %>">
79-
<%= helpers.format_text_value(champ.to_s.strip) %>
80-
</div>
81-
<% end %>
83+
84+
<%= render Dossiers::ChampPrefilledBadgeComponent.new(champ:, profile: @profile) %>
85+
</div>
8286
<% end %>
8387
<% end %>
8488
<% end %>
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# frozen_string_literal: true
2+
3+
require "rails_helper"
4+
5+
RSpec.describe Dossiers::ChampPrefilledBadgeComponent, type: :component do
6+
let(:types_de_champ_public) { [{ type: :text }] }
7+
let(:procedure) { create(:procedure, types_de_champ_public:) }
8+
let(:dossier) { create(:dossier, procedure:) }
9+
let(:champ) { dossier.champ_data.first }
10+
11+
subject { render_inline(described_class.new(champ:, profile:)) }
12+
13+
context 'as instructeur' do
14+
let(:profile) { 'instructeur' }
15+
16+
context 'when champ is prefilled and modified' do
17+
before do
18+
champ.update!(prefilled: true, value: 'modified', prefilled_original_value: { 'value' => 'original' })
19+
end
20+
21+
it 'renders warning text' do
22+
expect(subject).to have_css(".fr-message--warning", text: "Donnée du référentiel modifiée par l’usager")
23+
end
24+
end
25+
26+
context 'when champ is prefilled and not modified' do
27+
before do
28+
champ.update!(prefilled: true, value: 'original', prefilled_original_value: { 'value' => 'original' })
29+
end
30+
31+
it 'renders verified icon' do
32+
expect(subject).to have_css(".fr-icon-checkbox-circle-line")
33+
end
34+
end
35+
36+
context 'when champ is not prefilled' do
37+
it 'does not render' do
38+
expect(subject.to_html).to be_empty
39+
end
40+
end
41+
42+
context 'when champ has no prefilled_original_value (old dossier)' do
43+
before do
44+
champ.update!(prefilled: true, value: 'something')
45+
end
46+
47+
it 'does not render' do
48+
expect(subject.to_html).to be_empty
49+
end
50+
end
51+
end
52+
53+
context 'as usager' do
54+
let(:profile) { 'usager' }
55+
56+
context 'when champ is prefilled and modified' do
57+
before do
58+
champ.update!(prefilled: true, value: 'modified', prefilled_original_value: { 'value' => 'original' })
59+
end
60+
61+
it 'does not render' do
62+
expect(subject.to_html).to be_empty
63+
end
64+
end
65+
end
66+
end
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
describe 'Referentiel prefilled badge', js: true do
4+
let(:user) { create(:user) }
5+
let(:instructeur) { create(:instructeur) }
6+
let(:procedure) { create(:procedure, :published, :for_individual, :with_service, types_de_champ_public: [{ type: :text, libelle: 'Nom entreprise' }]) }
7+
let(:dossier) { create(:dossier, :en_construction, user:, procedure:) }
8+
let(:champ) { dossier.project_champs_public.first }
9+
10+
before do
11+
procedure.defaut_groupe_instructeur.add(instructeur)
12+
login_as instructeur.user, scope: :user
13+
end
14+
15+
scenario 'instructeur voit icone verifiee si valeur intacte' do
16+
champ.update!(prefilled: true, value: 'ACME Corp', prefilled_original_value: { 'value' => 'ACME Corp' })
17+
18+
visit instructeur_dossier_path(procedure, dossier)
19+
20+
expect(page).to have_css('.fr-icon-checkbox-circle-line')
21+
end
22+
23+
scenario 'instructeur voit warning si valeur changee' do
24+
champ.update!(prefilled: true, value: 'Modified Corp', prefilled_original_value: { 'value' => 'ACME Corp' })
25+
26+
visit instructeur_dossier_path(procedure, dossier)
27+
28+
expect(page).to have_text("Donnée du référentiel modifiée par l’usager")
29+
end
30+
31+
scenario 'pas de badge si pas prefilled' do
32+
champ.update!(value: 'Something')
33+
34+
visit instructeur_dossier_path(procedure, dossier)
35+
36+
expect(page).not_to have_css('.fr-icon-checkbox-circle-line')
37+
expect(page).not_to have_text("Donnée du référentiel modifiée par l'usager")
38+
end
39+
end

0 commit comments

Comments
 (0)