diff --git a/app/assets/stylesheets/02_utils.scss b/app/assets/stylesheets/02_utils.scss index 1e24f8b96..30a869ff3 100644 --- a/app/assets/stylesheets/02_utils.scss +++ b/app/assets/stylesheets/02_utils.scss @@ -87,7 +87,7 @@ // display .hidden { - display: none; + display: none !important; } .visible-on-previous-hover { diff --git a/app/components/dossiers/batch_operation_component.rb b/app/components/dossiers/batch_operation_component.rb index db9ac3573..596986bed 100644 --- a/app/components/dossiers/batch_operation_component.rb +++ b/app/components/dossiers/batch_operation_component.rb @@ -10,6 +10,21 @@ class Dossiers::BatchOperationComponent < ApplicationComponent ['a-suivre', 'traites', 'suivis'].include?(@statut) end + def operations_for_dossier(dossier) + case dossier.state + when Dossier.states.fetch(:en_construction) + [BatchOperation.operations.fetch(:passer_en_instruction)] + when Dossier.states.fetch(:en_instruction) + [BatchOperation.operations.fetch(:accepter)] + when Dossier.states.fetch(:accepte), Dossier.states.fetch(:refuse), Dossier.states.fetch(:sans_suite) + [BatchOperation.operations.fetch(:archiver)] + else + [] + end + end + + private + def available_operations case @statut when 'a-suivre' then diff --git a/app/components/dossiers/batch_operation_component/batch_operation_component.html.haml b/app/components/dossiers/batch_operation_component/batch_operation_component.html.haml index 5548797c8..380c49792 100644 --- a/app/components/dossiers/batch_operation_component/batch_operation_component.html.haml +++ b/app/components/dossiers/batch_operation_component/batch_operation_component.html.haml @@ -1,24 +1,24 @@ .batch-operation.fr-ml-auto.flex - if available_operations[:options].count.between?(1,3) - = form_for(BatchOperation.new, url: instructeur_batch_operations_path(procedure_id: procedure.id), method: :post, html: { class: 'form', id: "#{dom_id(BatchOperation.new)}" }) do |form| + = form_for(BatchOperation.new, url: instructeur_batch_operations_path(procedure_id: procedure.id), method: :post, html: { class: 'form', id: dom_id(BatchOperation.new) }) do |form| - available_operations[:options].each do |opt| - = render partial: "instructeurs/dossiers/batch_operation_inline_buttons", locals: { opt: opt, icons: icons, form: form } + = render Dossiers::BatchOperationInlineButtonsComponent.new(opt:, icons:, form:) - else - = form_for(BatchOperation.new, url: instructeur_batch_operations_path(procedure_id: procedure.id), method: :post, html: { class: 'form flex', id: "#{dom_id(BatchOperation.new)}" }) do |form| + = form_for(BatchOperation.new, url: instructeur_batch_operations_path(procedure_id: procedure.id), method: :post, html: { class: 'form flex', id: dom_id(BatchOperation.new) }) do |form| - available_operations[:options][0,2].each do |opt| - = render partial: "instructeurs/dossiers/batch_operation_inline_buttons", locals: { opt: opt, icons: icons, form: form } + = render Dossiers::BatchOperationInlineButtonsComponent.new(opt:, icons:, form:) .dropdown{ data: { controller: 'menu-button', popover: 'true' } } -# Dropdown button title - %button.fr-btn.fr-btn--sm.fr-ml-1w.dropdown-button{ disabled: true, data: { menu_button_target: 'button'} } + %button.fr-btn.fr-btn--sm.fr-ml-1w.dropdown-button{ disabled: true, data: { menu_button_target: 'button', batch_operation_target: 'menu' } } Autres actions multiples #state-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } } %ul.dropdown-items - available_operations[:options][2, available_operations[:options].count].each do |opt| %li{ 'data-turbo': 'true' } - = form.button opt[:label], class: 'dropdown-items-link ', disabled: true, name: "#{form.object_name}[operation]", value: opt[:operation] do + = form.button opt[:label], class: 'dropdown-items-link ', disabled: true, name: "#{form.object_name}[operation]", value: opt[:operation], data: { operation: opt[:operation] } do %span{ class: icons[opt[:operation].to_sym] } .dropdown-description %h4= opt[:label] diff --git a/app/components/dossiers/batch_operation_inline_buttons_component.rb b/app/components/dossiers/batch_operation_inline_buttons_component.rb new file mode 100644 index 000000000..bba90741f --- /dev/null +++ b/app/components/dossiers/batch_operation_inline_buttons_component.rb @@ -0,0 +1,9 @@ +class Dossiers::BatchOperationInlineButtonsComponent < ApplicationComponent + attr_reader :opt, :icons, :form + + def initialize(opt:, icons:, form:) + @opt = opt + @icons = icons + @form = form + end +end diff --git a/app/views/instructeurs/dossiers/_batch_operation_inline_buttons.html.haml b/app/components/dossiers/batch_operation_inline_buttons_component/batch_operation_inline_buttons_component.html.haml similarity index 73% rename from app/views/instructeurs/dossiers/_batch_operation_inline_buttons.html.haml rename to app/components/dossiers/batch_operation_inline_buttons_component/batch_operation_inline_buttons_component.html.haml index cd418ed03..1f4be9f4a 100644 --- a/app/views/instructeurs/dossiers/_batch_operation_inline_buttons.html.haml +++ b/app/components/dossiers/batch_operation_inline_buttons_component/batch_operation_inline_buttons_component.html.haml @@ -1,7 +1,7 @@ - if opt[:operation] == 'accepter' - .dropdown{ data: { controller: 'menu-button', popover: 'true' } } + .dropdown{ data: { controller: 'menu-button', popover: 'true', operation: opt[:operation] } } -# Dropdown button title - %button{ disabled: true, class: ['fr-btn fr-btn--sm fr-btn--icon-left fr-ml-1w', icons[opt[:operation].to_sym]], disabled: true, name: "#{form.object_name}[operation]" , data: { menu_button_target: 'button'} } + %button{ disabled: true, class: ['fr-btn fr-btn--sm fr-btn--icon-left fr-ml-1w', icons[opt[:operation].to_sym]], disabled: true, name: "#{form.object_name}[operation]" , data: { menu_button_target: 'button' } } = opt[:label] #state-menu.dropdown-content.fade-in-down{ data: { menu_button_target: 'menu' } } @@ -16,13 +16,13 @@ .motivation.accept = form.text_area :motivation, class: 'motivation-text-area' - .optional-justificatif{ id: "justificatif_motivation_suggest_accept", onclick: "DS.showImportJustificatif('accept');" } - .button Ajouter un justificatif (optionnel) - .hidden{ id: "justificatif_motivation_import_accept" } + #justificatif_motivation_suggest_accept.optional-justificatif + %button.button{ type: 'button', onclick: "DS.showImportJustificatif('accept');" } Ajouter un justificatif (optionnel) + #justificatif_motivation_import_accept.hidden = form.file_field :justificatif_motivation, direct_upload: true = form.button "Valider la décision", class: ['fr-btn fr-mt-2w'], disabled: true, name: "#{form.object_name}[operation]", value: opt[:operation] - else - = form.button opt[:label], class: ['fr-btn fr-btn--sm fr-btn--icon-left fr-ml-1w', icons[opt[:operation].to_sym]], disabled: true, name: "#{form.object_name}[operation]", value: opt[:operation] + = form.button opt[:label], class: ['fr-btn fr-btn--sm fr-btn--icon-left fr-ml-1w', icons[opt[:operation].to_sym]], disabled: true, name: "#{form.object_name}[operation]", value: opt[:operation], data: { operation: opt[:operation] } diff --git a/app/javascript/controllers/batch_operation_controller.ts b/app/javascript/controllers/batch_operation_controller.ts index dea4fffce..786da172d 100644 --- a/app/javascript/controllers/batch_operation_controller.ts +++ b/app/javascript/controllers/batch_operation_controller.ts @@ -1,9 +1,12 @@ import { ApplicationController } from './application_controller'; import { disable, enable } from '@utils'; +import invariant from 'tiny-invariant'; export class BatchOperationController extends ApplicationController { - static targets = ['input']; + static targets = ['menu', 'input']; + declare readonly menuTarget: HTMLButtonElement; + declare readonly hasMenuTarget: boolean; declare readonly inputTargets: HTMLInputElement[]; onCheckOne() { @@ -18,16 +21,42 @@ export class BatchOperationController extends ApplicationController { } toggleSubmitButtonWhenNeeded() { - const available = this.inputTargets.some((e) => e.checked); - const buttons = this.element.querySelectorAll( - '.batch-operation button' - ); - for (const button of buttons) { - if (available) { - enable(button); - } else { - disable(button); + const buttons = [ + ...this.element.querySelectorAll('[data-operation]') + ]; + const checked = this.inputTargets.filter((input) => input.checked); + if (checked.length) { + const available = buttons.filter((button) => { + const operation = button.dataset.operation; + invariant(operation, 'data-operation is required'); + const available = checked.every(isInputForOperation(operation)); + switchButton(button, available); + return available; + }); + if (this.hasMenuTarget) { + if (available.length) { + enable(this.menuTarget); + } else { + disable(this.menuTarget); + } } + } else if (this.hasMenuTarget) { + disable(this.menuTarget); } } } + +function isInputForOperation(operation: string) { + return (input: HTMLInputElement) => + (input.dataset.operations?.split(',') ?? []).includes(operation); +} + +function switchButton(button: HTMLButtonElement, flag: boolean) { + if (flag) { + enable(button); + button.querySelectorAll('button').forEach((button) => enable(button)); + } else { + disable(button); + button.querySelectorAll('button').forEach((button) => disable(button)); + } +} diff --git a/app/views/instructeurs/procedures/show.html.haml b/app/views/instructeurs/procedures/show.html.haml index 4e0cd990b..ef2b0ba56 100644 --- a/app/views/instructeurs/procedures/show.html.haml +++ b/app/views/instructeurs/procedures/show.html.haml @@ -131,7 +131,7 @@ - if p.batch_operation_id.present? = check_box_tag :"batch_operation[dossier_ids][]", p.dossier_id, true, disabled: true, id: dom_id(BatchOperation.new, "checkbox_#{p.dossier_id}"), aria: { label: t('views.instructeurs.dossiers.batch_operation.disabled') } - else - = check_box_tag :"batch_operation[dossier_ids][]", p.dossier_id, false, data: { batch_operation_target: "input", action: "batch-operation#onCheckOne" }, form: dom_id(BatchOperation.new), id: dom_id(BatchOperation.new, "checkbox_#{p.dossier_id}"), aria: { label: t('views.instructeurs.dossiers.batch_operation.enabled') } + = check_box_tag :"batch_operation[dossier_ids][]", p.dossier_id, false, data: { batch_operation_target: "input", action: "batch-operation#onCheckOne", operations: batch_operation_component.operations_for_dossier(p).join(',') }, form: dom_id(BatchOperation.new), id: dom_id(BatchOperation.new, "checkbox_#{p.dossier_id}"), aria: { label: t('views.instructeurs.dossiers.batch_operation.enabled') } - if @not_archived_notifications_dossier_ids.include?(p.dossier_id) %span.notifications{ 'aria-label': 'notifications' }