feat(dossier): autosave en construction

This commit is contained in:
Paul Chavard 2022-09-08 11:26:18 +02:00
parent d35ceb7214
commit d6f5be622c
14 changed files with 66 additions and 82 deletions

View file

@ -188,19 +188,15 @@ module Users
def update def update
@dossier = dossier_with_champs @dossier = dossier_with_champs
errors = update_dossier_and_compute_errors
if check_conditions? if errors.present?
assign_dossier_and_check_conditions flash.now.alert = errors
render :update_brouillon end
else
errors = update_dossier_and_compute_errors
if errors.present? respond_to do |format|
flash.now.alert = errors format.html { render :modifier }
render :modifier format.turbo_stream { render layout: false }
else
redirect_to demande_dossier_path(@dossier)
end
end end
end end
@ -308,18 +304,6 @@ module Users
private private
def check_conditions?
params[:check_conditions] && champs_params[:dossier]
end
def assign_dossier_and_check_conditions
@dossier.assign_attributes(champs_params[:dossier])
# We need to set dossier on champs, otherwise dossier will be reloaded
@dossier.champs.each do |champ|
champ.association(:dossier).target = @dossier
end
end
# if the status tab is filled, then this tab # if the status tab is filled, then this tab
# else first filled tab # else first filled tab
# else en-cours # else en-cours

View file

@ -42,7 +42,10 @@ export class AutosaveController extends ApplicationController {
this.#latestPromise = Promise.resolve(); this.#latestPromise = Promise.resolve();
this.onGlobal('autosave:retry', () => this.didRequestRetry()); this.onGlobal('autosave:retry', () => this.didRequestRetry());
this.on('change', (event) => this.onChange(event)); this.on('change', (event) => this.onChange(event));
this.on('input', (event) => this.onInput(event));
if (this.saveOnInput) {
this.on('input', (event) => this.onInput(event));
}
} }
disconnect() { disconnect() {
@ -80,7 +83,8 @@ export class AutosaveController extends ApplicationController {
this.debounce(this.enqueueAutosaveRequest, AUTOSAVE_DEBOUNCE_DELAY); this.debounce(this.enqueueAutosaveRequest, AUTOSAVE_DEBOUNCE_DELAY);
} else if ( } else if (
isSelectElement(target) || isSelectElement(target) ||
isCheckboxOrRadioInputElement(target) isCheckboxOrRadioInputElement(target) ||
(!this.saveOnInput && isTextInputElement(target))
) { ) {
this.enqueueAutosaveRequest(); this.enqueueAutosaveRequest();
} }
@ -99,6 +103,10 @@ export class AutosaveController extends ApplicationController {
} }
} }
private get saveOnInput() {
return !!this.form?.dataset.saveOnInput;
}
private didRequestRetry() { private didRequestRetry() {
if (this.#needsRetry) { if (this.#needsRetry) {
this.enqueueAutosaveRequest(); this.enqueueAutosaveRequest();

View file

@ -1,17 +0,0 @@
.autosave.autosave-state-idle{ data: { controller: 'autosave-status' } }
%p.autosave-explanation
%span.autosave-explanation-text
= t('views.instructeurs.dossiers.autosave.autosave_draft')
%p.autosave-status.succeeded
%span.autosave-icon.icon.accept
%span.autosave-label
= t('views.instructeurs.dossiers.autosave.autosave_confirmation')
%p.autosave-status.failed
%span.autosave-icon ⚠️
%span.autosave-label Impossible denregistrer les annotations
%button.button.small.autosave-retry{ type: :button, data: { action: 'autosave-status#onClickRetryButton', autosave_status_target: 'retryButton' } }
%span.autosave-retry-label réessayer
%span.autosave-retrying-label enregistrement en cours…

View file

@ -1,13 +1,19 @@
.autosave.autosave-state-idle{ data: { controller: 'autosave-status' } } .autosave.autosave-state-idle{ data: { controller: 'autosave-status' } }
%p.autosave-explanation %p.autosave-explanation
%span.autosave-explanation-text %span.autosave-explanation-text
= t('views.users.dossiers.autosave.autosave_draft') - if dossier.brouillon?
= link_to t('views.users.dossiers.autosave.more_infos'), FAQ_AUTOSAVE_URL, target: '_blank', rel: 'noopener', class: 'autosave-more-infos' = t('views.users.dossiers.autosave.draft_explanation')
- else
= t('views.users.dossiers.autosave.explanation')
= link_to t('views.users.dossiers.autosave.more_information'), FAQ_AUTOSAVE_URL, target: '_blank', rel: 'noopener', class: 'autosave-more-infos'
%p.autosave-status.succeeded %p.autosave-status.succeeded
%span.autosave-icon.icon.accept %span.autosave-icon.icon.accept
%span.autosave-label %span.autosave-label
= t('views.users.dossiers.autosave.autosave_confirmation') - if dossier.brouillon?
= t('views.users.dossiers.autosave.draft_confirmation')
- else
= t('views.users.dossiers.autosave.confirmation')
= link_to t('views.users.dossiers.autosave.more_information'), FAQ_AUTOSAVE_URL, target: '_blank', rel: 'noopener', class: 'autosave-more-infos' = link_to t('views.users.dossiers.autosave.more_information'), FAQ_AUTOSAVE_URL, target: '_blank', rel: 'noopener', class: 'autosave-more-infos'
%p.autosave-status.failed %p.autosave-status.failed

View file

@ -2,7 +2,7 @@
= render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier } = render partial: "shared/dossiers/submit_is_over", locals: { dossier: dossier }
- if dossier.brouillon? - if dossier.brouillon?
- form_options = { url: brouillon_dossier_url(dossier), method: :patch } - form_options = { url: brouillon_dossier_url(dossier), method: :patch, data: { save_on_input: true } }
- else - else
- form_options = { url: modifier_dossier_url(dossier), method: :patch } - form_options = { url: modifier_dossier_url(dossier), method: :patch }
@ -23,7 +23,7 @@
%hr %hr
- if dossier.show_groupe_instructeur_selector? - if dossier.show_groupe_instructeur_selector?
%span{ data: { controller: dossier.brouillon? ? 'autosave' : 'check-conditions' } } %span{ data: { controller: 'autosave' } }
= f.label :groupe_instructeur_id do = f.label :groupe_instructeur_id do
= dossier.procedure.routing_criteria_name = dossier.procedure.routing_criteria_name
%span.mandatory * %span.mandatory *
@ -38,20 +38,14 @@
- if !dossier.for_procedure_preview? - if !dossier.for_procedure_preview?
.dossier-edit-sticky-footer .dossier-edit-sticky-footer
.send-dossier-actions-bar .send-dossier-actions-bar
- if dossier.brouillon? = render partial: 'shared/dossiers/autosave', locals: { dossier: dossier }
= render partial: 'users/dossiers/autosave'
- if dossier.can_transition_to_en_construction? - if dossier.can_transition_to_en_construction?
= f.button t('views.shared.dossiers.edit.submit_dossier'), = f.button t('views.shared.dossiers.edit.submit_dossier'),
name: :submit_draft, name: :submit_draft,
value: true, value: true,
class: 'button send primary',
disabled: !current_user.owns?(dossier),
data: { 'disable-with': "Envoi en cours…" }
- else
= f.button t('views.shared.dossiers.edit.save_changes'),
class: 'button send primary', class: 'button send primary',
disabled: !current_user.owns?(dossier),
data: { 'disable-with': "Envoi en cours…" } data: { 'disable-with': "Envoi en cours…" }
- if dossier.brouillon? && !current_user.owns?(dossier) - if dossier.brouillon? && !current_user.owns?(dossier)

View file

@ -0,0 +1,5 @@
- @dossier.champs.filter(&:conditional?).each do |champ|
- if champ.visible?
= turbo_stream.show champ.input_group_id
- else
= turbo_stream.hide champ.input_group_id

View file

@ -137,7 +137,6 @@ en:
edit: edit:
autosave: Your file is automatically saved after each modification. You can close the window at any time and pick up where you left off later. autosave: Your file is automatically saved after each modification. You can close the window at any time and pick up where you left off later.
submit_dossier: Submit the file submit_dossier: Submit the file
save_changes: Save the changes of the file
messages: messages:
form: form:
send_message: "Send message" send_message: "Send message"
@ -151,9 +150,6 @@ en:
edit_identity: "Edit identity data" edit_identity: "Edit identity data"
instructeurs: instructeurs:
dossiers: dossiers:
autosave:
autosave_draft: Your annotations are automatically saved.
autosave_confirmation: Annotations saved
tab_steps: tab_steps:
to_follow: to follow to_follow: to follow
total: total total: total
@ -192,8 +188,10 @@ en:
dossiers: dossiers:
archived_dossier: "Your file will be kept %{duree_conservation_dossiers_dans_ds} more months" archived_dossier: "Your file will be kept %{duree_conservation_dossiers_dans_ds} more months"
autosave: autosave:
autosave_draft: Your draft is automatically saved. explanation: Your file is automatically saved.
autosave_confirmation: Draft saved confirmation: File saved
draft_explanation: Your draft is automatically saved.
draft_confirmation: Draft saved
more_information: More informations more_information: More informations
identite: identite:
identity_data: Identity data identity_data: Identity data

View file

@ -132,7 +132,6 @@ fr:
edit: edit:
autosave: Votre dossier est enregistré automatiquement après chaque modification. Vous pouvez à tout moment fermer la fenêtre et reprendre plus tard là où vous en étiez. autosave: Votre dossier est enregistré automatiquement après chaque modification. Vous pouvez à tout moment fermer la fenêtre et reprendre plus tard là où vous en étiez.
submit_dossier: Déposer le dossier submit_dossier: Déposer le dossier
save_changes: Enregistrer les modifications du dossier
messages: messages:
form: form:
send_message: "Envoyer le message" send_message: "Envoyer le message"
@ -146,9 +145,6 @@ fr:
edit_identity: "Modifier lidentité" edit_identity: "Modifier lidentité"
instructeurs: instructeurs:
dossiers: dossiers:
autosave:
autosave_draft: Vos annotations sont automatiquement enregistrées.
autosave_confirmation: Annotations enregistrées
tab_steps: tab_steps:
to_follow: à suivre to_follow: à suivre
total: au total total: au total
@ -188,10 +184,11 @@ fr:
dossiers: dossiers:
archived_dossier: "Votre dossier sera conservé %{duree_conservation_dossiers_dans_ds} mois supplémentaire" archived_dossier: "Votre dossier sera conservé %{duree_conservation_dossiers_dans_ds} mois supplémentaire"
autosave: autosave:
autosave_draft: Votre brouillon est automatiquement enregistré. explanation: Votre dossier est automatiquement enregistré.
autosave_confirmation: Brouillon enregistré confirmation: Dossier enregistré
more_information: More informations draft_explanation: Votre brouillon est automatiquement enregistré.
more_infos: En savoir plus draft_confirmation: Brouillon enregistré
more_information: En savoir plus
identite: identite:
identity_data: Données didentité identity_data: Données didentité
civility: Civilité civility: Civilité

View file

@ -649,7 +649,7 @@ describe Users::DossiersController, type: :controller do
expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction))
end end
it { is_expected.to redirect_to(demande_dossier_path(dossier)) } it { is_expected.to have_http_status(:ok) }
context 'when only a single file champ are modified' do context 'when only a single file champ are modified' do
# A bug in ActiveRecord causes records changed through grand-parent <-> parent <-> child # A bug in ActiveRecord causes records changed through grand-parent <-> parent <-> child
@ -726,7 +726,7 @@ describe Users::DossiersController, type: :controller do
it 'does not raise any errors' do it 'does not raise any errors' do
subject subject
expect(response).to redirect_to(demande_dossier_path(dossier)) expect(response).to have_http_status(:ok)
end end
end end
@ -741,7 +741,7 @@ describe Users::DossiersController, type: :controller do
it { expect(first_champ.reload.value).to eq('beautiful value') } it { expect(first_champ.reload.value).to eq('beautiful value') }
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) }
it { expect(response).to redirect_to(demande_dossier_path(dossier)) } it { expect(response).to have_http_status(:ok) }
end end
context 'when the dossier is followed by an instructeur' do context 'when the dossier is followed by an instructeur' do

View file

@ -120,7 +120,8 @@ describe 'The routing', js: true do
click_on 'Modifier mon dossier' click_on 'Modifier mon dossier'
fill_in litteraire_user.dossiers.first.champs.first.libelle, with: 'some value' fill_in litteraire_user.dossiers.first.champs.first.libelle, with: 'some value'
click_on 'Enregistrer les modifications du dossier' blur
expect(page).to have_css('span', text: 'Dossier enregistré', visible: true)
log_out log_out
# the litteraires instructeurs should have a notification # the litteraires instructeurs should have a notification
@ -214,7 +215,7 @@ describe 'The routing', js: true do
expect(page).not_to have_selector("option", text: "Groupe inactif") expect(page).not_to have_selector("option", text: "Groupe inactif")
select(new_group, from: 'dossier_groupe_instructeur_id') select(new_group, from: 'dossier_groupe_instructeur_id')
click_on "Enregistrer les modifications du dossier" expect(page).to have_css('span', text: 'Dossier enregistré', visible: true)
expect(page).to have_text(new_group) expect(page).to have_text(new_group)
log_out log_out

View file

@ -390,6 +390,9 @@ describe 'The user' do
expect(page).to have_no_css('label', text: 'tonnage', visible: true) expect(page).to have_no_css('label', text: 'tonnage', visible: true)
fill_in('age', with: '18') fill_in('age', with: '18')
blur
expect(page).to have_css('span', text: 'Dossier enregistré', visible: true)
# the champ keeps their previous value so they are all displayed # the champ keeps their previous value so they are all displayed
expect(page).to have_css('label', text: 'permis de conduire', visible: true) expect(page).to have_css('label', text: 'permis de conduire', visible: true)
expect(page).to have_css('label', text: 'tonnage', visible: true) expect(page).to have_css('label', text: 'tonnage', visible: true)

View file

@ -48,8 +48,10 @@ describe 'Dossier details:' do
expect(page).to have_current_path(dossier_path(dossier)) expect(page).to have_current_path(dossier_path(dossier))
end end
it_behaves_like 'the user can edit the submitted demande' context 'with js', js: true do
it_behaves_like 'the user can send messages to the instructeur' it_behaves_like 'the user can edit the submitted demande'
it_behaves_like 'the user can send messages to the instructeur'
end
private private

View file

@ -10,9 +10,12 @@ RSpec.shared_examples 'the user can edit the submitted demande' do
expect(page).to have_current_path(modifier_dossier_path(dossier)) expect(page).to have_current_path(modifier_dossier_path(dossier))
fill_in('Texte obligatoire', with: 'Nouveau texte') fill_in('Texte obligatoire', with: 'Nouveau texte')
click_on 'Enregistrer les modifications du dossier' blur
expect(page).to have_css('span', text: 'Dossier enregistré', visible: true)
click_on 'Demande'
expect(page).to have_current_path(demande_dossier_path(dossier)) expect(page).to have_current_path(demande_dossier_path(dossier))
expect(page).to have_content('Nouveau texte') expect(page).to have_content('Nouveau texte')
end end
end end

View file

@ -127,7 +127,7 @@ describe 'Invitations' do
expect(page).to have_text("user_invite@exemple.fr") expect(page).to have_text("user_invite@exemple.fr")
end end
context 'as an invited user' do context 'as an invited user', js: true do
before do before do
navigate_to_invited_dossier(invite) navigate_to_invited_dossier(invite)
expect(page).to have_current_path(dossier_path(invite.dossier)) expect(page).to have_current_path(dossier_path(invite.dossier))