diff --git a/app/components/editable_champ/carte_component.rb b/app/components/editable_champ/carte_component.rb index 864887f15..792995b35 100644 --- a/app/components/editable_champ/carte_component.rb +++ b/app/components/editable_champ/carte_component.rb @@ -9,4 +9,12 @@ class EditableChamp::CarteComponent < EditableChamp::EditableChampBaseComponent @autocomplete_component = EditableChamp::ComboSearchComponent.new(**args) end + + def update_path + if Champ.update_by_stable_id? + champs_carte_features_path(@champ.dossier, @champ.stable_id, row_id: @champ.row_id) + else + champs_legacy_carte_features_path(@champ) + end + end end diff --git a/app/components/editable_champ/carte_component/carte_component.html.haml b/app/components/editable_champ/carte_component/carte_component.html.haml index ad054669d..db14600f9 100644 --- a/app/components/editable_champ/carte_component/carte_component.html.haml +++ b/app/components/editable_champ/carte_component/carte_component.html.haml @@ -4,7 +4,7 @@ = react_component("MapEditor", { featureCollection: @champ.to_feature_collection, champId: @champ.input_id, - url: champs_carte_features_path(@champ), + url: update_path, options: @champ.render_options, autocompleteAnnounceTemplateId: @autocomplete_component.announce_template_id, autocompleteScreenReaderInstructions: t("combo_search_component.screen_reader_instructions") }, diff --git a/app/components/editable_champ/editable_champ_component.rb b/app/components/editable_champ/editable_champ_component.rb index 3e3177a19..8bc6f4cac 100644 --- a/app/components/editable_champ/editable_champ_component.rb +++ b/app/components/editable_champ/editable_champ_component.rb @@ -54,9 +54,17 @@ class EditableChamp::EditableChampComponent < ApplicationComponent def turbo_poll_url_value if @champ.private? - annotation_instructeur_dossier_path(@champ.dossier.procedure, @champ.dossier, @champ) + if Champ.update_by_stable_id? + annotation_instructeur_dossier_path(@champ.dossier.procedure, @champ.dossier, @champ.stable_id, row_id: @champ.row_id, with_public_id: true) + else + annotation_instructeur_dossier_path(@champ.dossier.procedure, @champ.dossier, @champ) + end else - champ_dossier_path(@champ.dossier, @champ) + if Champ.update_by_stable_id? + champ_dossier_path(@champ.dossier, @champ.stable_id, row_id: @champ.row_id, with_public_id: true) + else + champ_dossier_path(@champ.dossier, @champ) + end end end diff --git a/app/components/editable_champ/editable_champ_component/editable_champ_component.html.haml b/app/components/editable_champ/editable_champ_component/editable_champ_component.html.haml index 84c45bb17..9608eded7 100644 --- a/app/components/editable_champ/editable_champ_component/editable_champ_component.html.haml +++ b/app/components/editable_champ/editable_champ_component/editable_champ_component.html.haml @@ -7,4 +7,7 @@ = render Dsfr::InputStatusMessageComponent.new(errors_on_attribute: champ_component.errors_on_attribute?, error_full_messages: champ_component.error_full_messages, describedby_id: @champ.describedby_id, champ: @champ) - = @form.hidden_field :id, value: @champ.id + - if Champ.update_by_stable_id? + = @form.hidden_field :with_public_id, value: 'true' + - else + = @form.hidden_field :id, value: @champ.id diff --git a/app/components/editable_champ/multiple_drop_down_list_component.rb b/app/components/editable_champ/multiple_drop_down_list_component.rb index 291ed3e77..9ba731618 100644 --- a/app/components/editable_champ/multiple_drop_down_list_component.rb +++ b/app/components/editable_champ/multiple_drop_down_list_component.rb @@ -8,4 +8,12 @@ class EditableChamp::MultipleDropDownListComponent < EditableChamp::EditableCham def dsfr_champ_container @champ.render_as_checkboxes? ? :fieldset : :div end + + def update_path(option) + if Champ.update_by_stable_id? + champs_options_path(@champ.dossier, @champ.stable_id, row_id: @champ.row_id, option:) + else + champs_legacy_options_path(@champ, option:) + end + end end diff --git a/app/components/editable_champ/multiple_drop_down_list_component/multiple_drop_down_list_component.html.haml b/app/components/editable_champ/multiple_drop_down_list_component/multiple_drop_down_list_component.html.haml index f3d8007b9..609e0d690 100644 --- a/app/components/editable_champ/multiple_drop_down_list_component/multiple_drop_down_list_component.html.haml +++ b/app/components/editable_champ/multiple_drop_down_list_component/multiple_drop_down_list_component.html.haml @@ -13,7 +13,7 @@ - if @champ.selected_options.present? .fr-mb-2w.fr-mt-2w{ "data-turbo": "true" } - @champ.selected_options.each do |option| - = render NestedForms::OwnedButtonComponent.new(formaction: champs_options_path(@champ.id, option:), http_method: :delete, opt: { aria: {pressed: true }, class: 'fr-tag fr-tag-bug fr-mb-1w fr-mr-1w', id: @champ.checkbox_id(option) }) do + = render NestedForms::OwnedButtonComponent.new(formaction: update_path(option), http_method: :delete, opt: { aria: {pressed: true }, class: 'fr-tag fr-tag-bug fr-mb-1w fr-mr-1w', id: @champ.checkbox_id(option) }) do = option - if @champ.unselected_options.present? = @form.select :value, @champ.unselected_options, { selected: '', include_blank: false, prompt: t('.prompt') }, id: @champ.input_id, aria: { describedby: @champ.describedby_id }, class: 'fr-select fr-mt-2v' diff --git a/app/components/editable_champ/rna_component.rb b/app/components/editable_champ/rna_component.rb index 1742676eb..09783147c 100644 --- a/app/components/editable_champ/rna_component.rb +++ b/app/components/editable_champ/rna_component.rb @@ -1,5 +1,13 @@ class EditableChamp::RNAComponent < EditableChamp::EditableChampBaseComponent def dsfr_input_classname 'fr-input' + end + + def update_path + if Champ.update_by_stable_id? + champs_rna_path(@champ.dossier, @champ.stable_id, row_id: @champ.row_id) + else + champs_legacy_rna_path(@champ) end + end end diff --git a/app/components/editable_champ/rna_component/rna_component.html.haml b/app/components/editable_champ/rna_component/rna_component.html.haml index 0543591ad..4d7240518 100644 --- a/app/components/editable_champ/rna_component/rna_component.html.haml +++ b/app/components/editable_champ/rna_component/rna_component.html.haml @@ -1,4 +1,4 @@ -= @form.text_field(:value, input_opts( id: @champ.input_id, aria: { describedby: @champ.describedby_id }, data: { controller: 'turbo-input', turbo_input_load_on_connect_value: @champ.prefilled? && @champ.value.present? && @champ.data.blank?, turbo_input_url_value: champs_rna_path(@champ.id) }, required: @champ.required?, pattern: "W[0-9]{9}", class: "width-33-desktop", maxlength: 10)) += @form.text_field(:value, input_opts( id: @champ.input_id, aria: { describedby: @champ.describedby_id }, data: { controller: 'turbo-input', turbo_input_load_on_connect_value: @champ.prefilled? && @champ.value.present? && @champ.data.blank?, turbo_input_url_value: update_path }, required: @champ.required?, pattern: "W[0-9]{9}", class: "width-33-desktop", maxlength: 10)) .rna-info{ id: dom_id(@champ, :rna_info) } = render 'shared/champs/rna/association', champ: @champ, error: nil diff --git a/app/components/editable_champ/siret_component.rb b/app/components/editable_champ/siret_component.rb index 8997b7c4e..800d6be5d 100644 --- a/app/components/editable_champ/siret_component.rb +++ b/app/components/editable_champ/siret_component.rb @@ -1,7 +1,7 @@ class EditableChamp::SiretComponent < EditableChamp::EditableChampBaseComponent def dsfr_input_classname 'fr-input' - end + end def hint_id dom_id(@champ, :siret_info) @@ -10,4 +10,12 @@ class EditableChamp::SiretComponent < EditableChamp::EditableChampBaseComponent def hintable? true end + + def update_path + if Champ.update_by_stable_id? + champs_siret_path(@champ.dossier, @champ.stable_id, row_id: @champ.row_id) + else + champs_legacy_siret_path(@champ) + end + end end diff --git a/app/components/editable_champ/siret_component/siret_component.html.haml b/app/components/editable_champ/siret_component/siret_component.html.haml index ae999e84a..9e0745c79 100644 --- a/app/components/editable_champ/siret_component/siret_component.html.haml +++ b/app/components/editable_champ/siret_component/siret_component.html.haml @@ -1,4 +1,4 @@ -= @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, data: { controller: 'turbo-input', turbo_input_load_on_connect_value: @champ.prefilled? && @champ.value.present? && @champ.etablissement.blank?, turbo_input_url_value: champs_siret_path(@champ.id) }, required: @champ.required?, pattern: "[0-9]{14}", class: "width-33-desktop", maxlength: 14)) += @form.text_field(:value, input_opts(id: @champ.input_id, aria: { describedby: @champ.describedby_id }, data: { controller: 'turbo-input', turbo_input_load_on_connect_value: @champ.prefilled? && @champ.value.present? && @champ.etablissement.blank?, turbo_input_url_value: update_path }, required: @champ.required?, pattern: "[0-9]{14}", class: "width-33-desktop", maxlength: 14)) .siret-info{ id: dom_id(@champ, :siret_info) } - if @champ.etablissement.present? = render EditableChamp::EtablissementTitreComponent.new(etablissement: @champ.etablissement) diff --git a/app/controllers/champs/options_controller.rb b/app/controllers/champs/options_controller.rb index 4c864a244..cc878fc3e 100644 --- a/app/controllers/champs/options_controller.rb +++ b/app/controllers/champs/options_controller.rb @@ -3,6 +3,7 @@ class Champs::OptionsController < Champs::ChampController def remove @champ.remove_option([params[:option]].compact, true) + @champ.reload @dossier = @champ.private? ? nil : @champ.dossier champs_attributes = { @champ.public_id => params[:champ_id].present? ? { id: @champ.id } : { with_public_id: true } } @to_show, @to_hide, @to_update = champs_to_turbo_update(champs_attributes, @champ.dossier.champs) diff --git a/app/controllers/instructeurs/dossiers_controller.rb b/app/controllers/instructeurs/dossiers_controller.rb index 8063e0438..2f2f1b51d 100644 --- a/app/controllers/instructeurs/dossiers_controller.rb +++ b/app/controllers/instructeurs/dossiers_controller.rb @@ -295,11 +295,12 @@ module Instructeurs def annotation @dossier = dossier_with_champs(pj_template: false) + annotation_id_or_stable_id = params[:stable_id] annotation = if params[:with_public_id].present? - type_de_champ = @dossier.find_type_de_champ_by_stable_id(params[:annotation_id], :private) + type_de_champ = @dossier.find_type_de_champ_by_stable_id(annotation_id_or_stable_id, :private) @dossier.project_champ(type_de_champ, params[:row_id]) else - @dossier.champs_private_all.find(params[:annotation_id]) + @dossier.champs_private_all.find(annotation_id_or_stable_id) end respond_to do |format| diff --git a/app/controllers/users/dossiers_controller.rb b/app/controllers/users/dossiers_controller.rb index c80792af4..921a06013 100644 --- a/app/controllers/users/dossiers_controller.rb +++ b/app/controllers/users/dossiers_controller.rb @@ -317,11 +317,12 @@ module Users def champ @dossier = dossier_with_champs(pj_template: false) + champ_id_or_stable_id = params[:stable_id] champ = if params[:with_public_id].present? - type_de_champ = @dossier.find_type_de_champ_by_stable_id(params[:champ_id], :public) + type_de_champ = @dossier.find_type_de_champ_by_stable_id(champ_id_or_stable_id, :public) @dossier.project_champ(type_de_champ, params[:row_id]) else - @dossier.champs_public_all.find(params[:champ_id]) + @dossier.champs_public_all.find(champ_id_or_stable_id) end respond_to do |format| diff --git a/app/helpers/champ_helper.rb b/app/helpers/champ_helper.rb index 61c988788..a79aada8d 100644 --- a/app/helpers/champ_helper.rb +++ b/app/helpers/champ_helper.rb @@ -9,7 +9,11 @@ module ChampHelper def auto_attach_url(object, params = {}) if object.is_a?(Champ) - champs_attach_piece_justificative_url(object.id, params) + if Champ.update_by_stable_id? + champs_piece_justificative_url(object.dossier, object.stable_id, params.merge(row_id: object.row_id)) + else + champs_legacy_piece_justificative_url(object.id, params) + end elsif object.is_a?(TypeDeChamp) && object.piece_justificative? piece_justificative_template_admin_procedure_type_de_champ_url(stable_id: object.stable_id, procedure_id: object.procedure.id, **params) elsif object.is_a?(TypeDeChamp) && object.explication? diff --git a/app/javascript/components/MapEditor/hooks.ts b/app/javascript/components/MapEditor/hooks.ts index eae70cb52..78b6a9267 100644 --- a/app/javascript/components/MapEditor/hooks.ts +++ b/app/javascript/components/MapEditor/hooks.ts @@ -137,7 +137,7 @@ export function useFeatureCollection( for (const feature of features) { const id = feature.properties?.id; if (id) { - await httpRequest(`${url}/${id}`, { + await httpRequest(endpointWithId(url, id), { method: 'patch', json: { feature } }).json(); @@ -174,7 +174,9 @@ export function useFeatureCollection( const deletedFeatures = []; for (const feature of features) { const id = feature.properties?.id; - await httpRequest(`${url}/${id}`, { method: 'delete' }).json(); + await httpRequest(endpointWithId(url, id), { + method: 'delete' + }).json(); deletedFeatures.push(feature); } removeFeatures(deletedFeatures, external); @@ -212,3 +214,11 @@ function useError(): [string | undefined, (message: string) => void] { return [error, onError]; } + +// We need this because endoint can have query params. For example with /champs/123?row_id=abc we can't juste concatanate id. +// We want /champs/123/456?row_id=abc not /champs/123?row_id=abc/456 +function endpointWithId(endpoint: string, id: string) { + const url = new URL(endpoint, document.baseURI); + url.pathname = `${url.pathname}/${id}`; + return url.toString(); +} diff --git a/app/models/champ.rb b/app/models/champ.rb index 7c98eebcf..cbff3b54a 100644 --- a/app/models/champ.rb +++ b/app/models/champ.rb @@ -290,6 +290,10 @@ class Champ < ApplicationRecord self.value = value.delete("\u0000") end + def self.update_by_stable_id? + Flipper.enabled?(:champ_update_by_stable_id, Current.user) + end + class NotImplemented < ::StandardError def initialize(method) super(":#{method} not implemented") diff --git a/app/views/shared/_piece_justificative_template.html.haml b/app/views/shared/_piece_justificative_template.html.haml index abb90edbf..1f45bb7ab 100644 --- a/app/views/shared/_piece_justificative_template.html.haml +++ b/app/views/shared/_piece_justificative_template.html.haml @@ -1 +1 @@ -= render Dsfr::DownloadComponent.new(attachment: champ.type_de_champ.piece_justificative_template, url: champs_piece_justificative_template_path(champ), name: "Modèle à télécharger", ephemeral_link: administrateur_signed_in? ) += render Dsfr::DownloadComponent.new(attachment: champ.type_de_champ.piece_justificative_template, url: champs_piece_justificative_template_path(champ.dossier, champ.stable_id, row_id: champ.row_id), name: "Modèle à télécharger", ephemeral_link: administrateur_signed_in? ) diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb index 9b22801b9..7eedd85e7 100644 --- a/config/initializers/flipper.rb +++ b/config/initializers/flipper.rb @@ -30,7 +30,8 @@ features = [ :groupe_instructeur_api_hack, :hide_instructeur_email, :sva, - :switch_domain + :switch_domain, + :champ_update_by_stable_id ] def database_exists? diff --git a/config/routes.rb b/config/routes.rb index 9256e08a2..fb0bc9302 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -196,32 +196,32 @@ Rails.application.routes.draw do post ':dossier_id/:stable_id/repetition', to: 'repetition#add', as: :repetition delete ':dossier_id/:stable_id/repetition', to: 'repetition#remove' - get ':dossier_id/:stable_id/siret', to: 'siret#show' - get ':dossier_id/:stable_id/rna', to: 'rna#show' - delete ':dossier_id/:stable_id/options', to: 'options#remove' + get ':dossier_id/:stable_id/siret', to: 'siret#show', as: :siret + get ':dossier_id/:stable_id/rna', to: 'rna#show', as: :rna + delete ':dossier_id/:stable_id/options', to: 'options#remove', as: :options - get ':dossier_id/:stable_id/carte/features', to: 'carte#index' + get ':dossier_id/:stable_id/carte/features', to: 'carte#index', as: :carte_features post ':dossier_id/:stable_id/carte/features', to: 'carte#create' - patch ':dossier_id/:stable_id/carte/features/:id', to: 'carte#update' + patch ':dossier_id/:stable_id/carte/features/:id', to: 'carte#update', as: :carte_feature delete ':dossier_id/:stable_id/carte/features/:id', to: 'carte#destroy' - get ':dossier_id/:stable_id/piece_justificative', to: 'piece_justificative#show' + get ':dossier_id/:stable_id/piece_justificative', to: 'piece_justificative#show', as: :piece_justificative put ':dossier_id/:stable_id/piece_justificative', to: 'piece_justificative#update' - get ':dossier_id/:stable_id/piece_justificative/template', to: 'piece_justificative#template' + get ':dossier_id/:stable_id/piece_justificative/template', to: 'piece_justificative#template', as: :piece_justificative_template # TODO: remove after migration is ower - get ':champ_id/siret', to: 'siret#show', as: :siret - get ':champ_id/rna', to: 'rna#show', as: :rna - delete ':champ_id/options', to: 'options#remove', as: :options + get ':champ_id/siret', to: 'siret#show', as: :legacy_siret + get ':champ_id/rna', to: 'rna#show', as: :legacy_rna + delete ':champ_id/options', to: 'options#remove', as: :legacy_options - get ':champ_id/carte/features', to: 'carte#index', as: :carte_features + get ':champ_id/carte/features', to: 'carte#index', as: :legacy_carte_features post ':champ_id/carte/features', to: 'carte#create' patch ':champ_id/carte/features/:id', to: 'carte#update' delete ':champ_id/carte/features/:id', to: 'carte#destroy' - get ':champ_id/piece_justificative', to: 'piece_justificative#show', as: :piece_justificative - put ':champ_id/piece_justificative', to: 'piece_justificative#update', as: :attach_piece_justificative - get ':champ_id/piece_justificative/template', to: 'piece_justificative#template', as: :piece_justificative_template + get ':champ_id/piece_justificative', to: 'piece_justificative#show', as: :legacy_piece_justificative + put ':champ_id/piece_justificative', to: 'piece_justificative#update' + get ':champ_id/piece_justificative/template', to: 'piece_justificative#template' end resources :attachments, only: [:show, :destroy] @@ -373,7 +373,7 @@ Rails.application.routes.draw do get 'modifier', to: 'dossiers#modifier' post 'modifier', to: 'dossiers#submit_en_construction' patch 'modifier', to: 'dossiers#modifier_legacy' - get 'champs/:champ_id', to: 'dossiers#champ', as: :champ + get 'champs/:stable_id', to: 'dossiers#champ', as: :champ get 'merci' get 'demande' get 'messagerie' @@ -497,7 +497,7 @@ Rails.application.routes.draw do get 'avis' get 'avis_new' get 'personnes-impliquees' => 'dossiers#personnes_impliquees' - get 'annotations/:annotation_id', to: 'dossiers#annotation', as: :annotation + get 'annotations/:stable_id', to: 'dossiers#annotation', as: :annotation patch 'follow' patch 'unfollow' patch 'archive' diff --git a/spec/controllers/champs/piece_justificative_controller_spec.rb b/spec/controllers/champs/piece_justificative_controller_spec.rb index 26b1a2e21..0c0eba1e1 100644 --- a/spec/controllers/champs/piece_justificative_controller_spec.rb +++ b/spec/controllers/champs/piece_justificative_controller_spec.rb @@ -11,7 +11,8 @@ describe Champs::PieceJustificativeController, type: :controller do subject do put :update, params: { position: '1', - champ_id: champ.id, + dossier_id: champ.dossier_id, + stable_id: champ.stable_id, blob_signed_id: file }.compact, format: :turbo_stream end @@ -73,7 +74,8 @@ describe Champs::PieceJustificativeController, type: :controller do subject do get :template, params: { - champ_id: champ.id + dossier_id: champ.dossier_id, + stable_id: champ.stable_id } end diff --git a/spec/controllers/champs/rna_controller_spec.rb b/spec/controllers/champs/rna_controller_spec.rb index 1c738f794..a9275e843 100644 --- a/spec/controllers/champs/rna_controller_spec.rb +++ b/spec/controllers/champs/rna_controller_spec.rb @@ -13,7 +13,8 @@ describe Champs::RNAController, type: :controller do end let(:params) do { - champ_id: champ.id, + dossier_id: champ.dossier_id, + stable_id: champ.stable_id, dossier: { champs_public_attributes: champs_public_attributes }