Skip to content

Commit 78d09e7

Browse files
mfoclaude
andcommitted
feat: RevertPrefilledButtonComponent + view integration + spec + route + controller revert_prefill + specs
Add PATCH champs/:stable_id/revert_prefill route and controller action. Restores the prefilled original value via turbo_stream re-render. Guarded by ensure_ownership_or_invitation! and ensure_dossier_can_be_updated. Add revert button visible when user modifies a prefilled champ. Uses DSFR tertiary button style, triggers turbo_stream PATCH to revert_prefill action. Integrated in editable_champ_component after InputStatusMessage. End-to-end tests verifying: - Revert button appears when prefilled value is modified - Revert button does not appear when value is unchanged Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent cc30363 commit 78d09e7

12 files changed

Lines changed: 210 additions & 5 deletions

app/components/editable_champ/editable_champ_component.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,14 @@ def html_options
7979
end
8080

8181
def fieldset_element_attributes
82-
{
82+
attributes = {
8383
id: @champ.input_group_id,
8484
"hidden": !@champ.visible?,
8585
}
86+
if @champ.prefilled?
87+
attributes[:data] = { turbo_force: :server }
88+
end
89+
attributes
8690
end
8791

8892
def stimulus_values

app/components/editable_champ/editable_champ_component/editable_champ_component.html.haml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
66
= render champ_component
77
= render Dsfr::InputStatusMessageComponent.new(champ_component:champ_component, champ: @champ, row_number: row_number_if_in_repetition)
8+
= render EditableChamp::RevertPrefilledButtonComponent.new(champ: @champ)
89
= render EditableChamp::ReferentielDisplayComponent.new(champ: @champ)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
class EditableChamp::RevertPrefilledButtonComponent < ApplicationComponent
4+
def initialize(champ:)
5+
@champ = champ
6+
end
7+
8+
def render?
9+
!@champ.private? && !@champ.instructeur_buffer_stream? && @champ.prefilled_value_modified?
10+
end
11+
12+
def revert_path
13+
revert_prefill_champ_dossier_path(@champ.dossier, stable_id: @champ.stable_id)
14+
end
15+
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
en:
3+
revert_label: "Autofill again"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
fr:
3+
revert_label: "Remplir à nouveau automatiquement"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<%= render NestedForms::OwnedButtonComponent.new(formaction: revert_path, http_method: :patch, opt: { class: "fr-btn fr-btn--tertiary fr-btn--sm fr-icon-refresh-line fr-btn--icon-left fr-mt-1w" }) do %>
2+
<%= t('.revert_label') %>
3+
<% end %>

app/components/nested_forms/form_owner_component.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# see: from attribute & formaction
99

1010
class NestedForms::FormOwnerComponent < ApplicationComponent
11-
HTTP_METHODS = [:create, :delete]
11+
HTTP_METHODS = [:create, :delete, :patch]
1212

1313
private
1414

app/controllers/users/dossiers_controller.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class DossiersController < UserController
1212
layout 'procedure_context', only: [:identite, :update_identite, :siret, :update_siret]
1313

1414
ACTIONS_ALLOWED_TO_ANY_USER = [:index, :new, :deleted_dossiers, :trash, :transfer_requests]
15-
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :modifier, :update, :create_commentaire, :attestation_depot, :restore, :champ, :check_completude, :notify_owner_for_changes]
15+
ACTIONS_ALLOWED_TO_OWNER_OR_INVITE = [:show, :destroy, :demande, :messagerie, :brouillon, :modifier, :update, :create_commentaire, :attestation_depot, :restore, :champ, :check_completude, :notify_owner_for_changes, :revert_prefill]
1616
TRASH_ACTIONS = [:show_in_trash, :show_deleted]
1717
ITEMS_PER_PAGE = 25
1818
SIMPLE_LIST_THRESHOLD = 5
@@ -22,14 +22,14 @@ class DossiersController < UserController
2222
before_action :ensure_ownership!, except: ACTIONS_ALLOWED_TO_ANY_USER + ACTIONS_ALLOWED_TO_OWNER_OR_INVITE + TRASH_ACTIONS
2323
before_action :redirect_if_hidden_or_deleted_dossier, only: [:show]
2424
before_action :ensure_ownership_or_invitation!, only: ACTIONS_ALLOWED_TO_OWNER_OR_INVITE
25-
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_siret, :brouillon, :submit_brouillon, :submit_en_construction, :modifier, :update, :champ]
25+
before_action :ensure_dossier_can_be_updated, only: [:update_identite, :update_siret, :brouillon, :submit_brouillon, :submit_en_construction, :modifier, :update, :champ, :revert_prefill]
2626
before_action :ensure_dossier_can_be_filled, only: [:brouillon, :modifier, :submit_brouillon, :submit_en_construction, :update]
2727
before_action :ensure_dossier_can_be_viewed, only: [:show]
2828
before_action :ensure_pro_connect_for_procedure, only: [:brouillon, :modifier, :submit_brouillon, :submit_en_construction, :update]
2929
before_action :ensure_editing_brouillon, only: [:brouillon]
3030
before_action :forbid_closed_submission!, only: [:submit_brouillon]
3131
before_action :ensure_dossier_has_changes, only: [:submit_en_construction], if: :update_with_stream?
32-
before_action :set_dossier_stream, only: [:modifier, :update, :submit_en_construction, :check_completude, :champ], if: :update_with_stream?
32+
before_action :set_dossier_stream, only: [:modifier, :update, :submit_en_construction, :check_completude, :champ, :revert_prefill], if: :update_with_stream?
3333
before_action :show_demarche_en_test_banner
3434
before_action :store_user_location!, only: :new
3535
before_action :set_default_value_for_france_connect_champs, only: [:brouillon, :modifier]
@@ -364,6 +364,21 @@ def champ
364364
end
365365
end
366366

367+
def revert_prefill
368+
@dossier = dossier_with_champs(pj_template: false)
369+
champ = @dossier.public_champ_for_update(params[:stable_id], updated_by: current_user.email)
370+
371+
champ.revert_to_prefilled_value! if champ.prefilled_original_value.present?
372+
373+
respond_to do |format|
374+
format.turbo_stream do
375+
@to_show, @to_hide = []
376+
@to_update = [champ]
377+
render :update, layout: false
378+
end
379+
end
380+
end
381+
367382
def create_commentaire
368383
@commentaire = CommentaireService.create(current_user, dossier, commentaire_params)
369384

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@
384384
post 'check_completude', to: 'dossiers#check_completude'
385385
post 'notify_owner_for_changes', to: 'dossiers#notify_owner_for_changes'
386386
get 'champs/:stable_id', to: 'dossiers#champ', as: :champ
387+
patch 'champs/:stable_id/revert_prefill', to: 'dossiers#revert_prefill', as: :revert_prefill_champ
387388
get 'merci'
388389
get 'demande'
389390
get 'messagerie'
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# frozen_string_literal: true
2+
3+
require "rails_helper"
4+
5+
RSpec.describe EditableChamp::RevertPrefilledButtonComponent, 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:)) }
12+
13+
context 'when champ is prefilled and modified' do
14+
before do
15+
champ.update!(prefilled: true, value: 'modified', prefilled_original_value: { 'value' => 'original' })
16+
end
17+
18+
it 'renders the revert button' do
19+
expect(subject).to have_button("Remplir à nouveau automatiquement")
20+
end
21+
end
22+
23+
context 'when champ is prefilled but not modified' do
24+
before do
25+
champ.update!(prefilled: true, value: 'original', prefilled_original_value: { 'value' => 'original' })
26+
end
27+
28+
it 'does not render' do
29+
expect(subject.to_html).to be_empty
30+
end
31+
end
32+
33+
context 'when champ is not prefilled' do
34+
it 'does not render' do
35+
expect(subject.to_html).to be_empty
36+
end
37+
end
38+
39+
context 'when champ is private (annotation)' do
40+
let(:types_de_champ_private) { [{ type: :text }] }
41+
let(:procedure) { create(:procedure, types_de_champ_private:) }
42+
let(:champ) { dossier.champ_data.find(&:private?) }
43+
44+
before do
45+
champ.update!(prefilled: true, value: 'modified', prefilled_original_value: { 'value' => 'original' })
46+
end
47+
48+
it 'does not render' do
49+
expect(subject.to_html).to be_empty
50+
end
51+
end
52+
53+
context 'when champ is on instructeur buffer stream' do
54+
before do
55+
champ.update!(prefilled: true, value: 'modified', prefilled_original_value: { 'value' => 'original' }, stream: Dossier::INSTRUCTEUR_BUFFER_STREAM)
56+
end
57+
58+
it 'does not render' do
59+
expect(subject.to_html).to be_empty
60+
end
61+
end
62+
end

0 commit comments

Comments
 (0)