Skip to content

Commit

Permalink
Merge branch 'production' into frf/plages-ouverture-meilleur-libelle-…
Browse files Browse the repository at this point in the history
…par-defaut
  • Loading branch information
francois-ferrandis authored Dec 5, 2024
2 parents bd5a324 + 6aba2c4 commit 2a96bd1
Show file tree
Hide file tree
Showing 19 changed files with 468 additions and 144 deletions.
45 changes: 45 additions & 0 deletions app/controllers/admin/territories/motifs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,51 @@ def index
end
end

def batch_edit
@motifs = Motif.where(id: params[:motif_ids])
@motifs.each do |motif|
authorize(motif, :edit?, policy_class: Agent::MotifPolicy)
end
end

UPDATABLE_ATTRS = %i[
name
service_id
default_duration_in_min
color
restriction_for_rdv
instruction_for_rdv
custom_cancel_warning_message
].freeze

def batch_update # rubocop:disable Metrics/PerceivedComplexity
@motifs = Motif.where(id: params[:motif_ids])
@motifs.each do |motif|
authorize(motif, :update?, policy_class: Agent::MotifPolicy)
end

permitted_params = params.permit(*UPDATABLE_ATTRS).compact_blank
Motif.transaction do
@motifs.each do |motif|
motif.update(permitted_params)
end

raise ActiveRecord::Rollback unless @motifs.all?(&:valid?)
end

invalid_motifs = @motifs.select(&:invalid?)

if invalid_motifs.none?
flash[:success] = "Motifs modifiés"
else
flash[:error] = invalid_motifs.map do |invalid_motif|
"Motif #{invalid_motif.id} : #{invalid_motif.errors.full_messages.join(', ')}"
end.join("<br>")
end

redirect_to batch_edit_admin_territory_motifs_path(motif_ids: params[:motif_ids])
end

def new
skip_authorization
@motif = Motif.new
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/static_pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ def contact
def domaines; end

def presentation_for_agents
render current_domain.presentation_for_agents_template_name, layout: "application_base"
if current_domain == Domain::RDV_MAIRIE
redirect_to root_path # La landing page pour RDV Service Public s'adresse aux agents
else
render current_domain.presentation_for_agents_template_name, layout: "application_base"
end
end

def microsoft_domain_verification
Expand Down
6 changes: 1 addition & 5 deletions app/form_models/admin/create_motifs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ def organisations_are_present
def motifs_are_valid
motifs.select(&:invalid?).each do |motif|
motif.errors.each do |motif_error|
if motif_error.attribute == :name && motif_error.type == :taken
errors.add(:base, "Un motif du même nom, même service et même type existe déjà dans #{motif.organisation.name}")
else
errors.import(motif_error) unless errors.added?(motif_error.attribute, motif_error.type) # deduplicate errors
end
errors.import(motif_error) unless errors.added?(motif_error.attribute, motif_error.type, **motif_error.options) # deduplicate errors
end
end
end
Expand Down
1 change: 1 addition & 0 deletions app/javascript/application_agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { DestroyButton } from './components/destroy-button'
import { Tooltips } from './components/tooltips'
import { PlageOuverture } from './components/plage_ouverture.js'
import {CheckAll, UnCheckAll} from './components/check-all'
import './components/motifs_table'
import './components/calendar'
import './components/browser-detection'
import './components/clear-field-on-focus.js'
Expand Down
29 changes: 29 additions & 0 deletions app/javascript/components/motifs_table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
document.addEventListener("turbolinks:load", function () {
batchEditCheckboxes().forEach(e => e.addEventListener("change", refreshButtonState))

const trigger = triggerCheckbox()
if(trigger) {
trigger.addEventListener("change", event => {
batchEditCheckboxes().forEach(input => input.checked = trigger.checked)
refreshButtonState()
})
}
});

function refreshButtonState() {
const disabled = Array.from(batchEditCheckboxes()).filter(c => c.checked).length < 2;
batchEditButton().disabled = disabled;
batchEditButton().classList.toggle("btn-outline-primary", !disabled)
}

function batchEditCheckboxes() {
return document.querySelectorAll(".js-batch-edit-checkbox")
}

function batchEditButton() {
return document.querySelector(".js-batch-edit-button")
}

function triggerCheckbox() {
return document.querySelector(".js-trigger-checkbox")
}
2 changes: 1 addition & 1 deletion app/models/domain.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class Domain
public_logo_path: "/logo_rdv_service_public.png",
dark_logo_path: "logos/logo_sombre_rdv_service_public.svg",
name: "RDV Service Public",
presentation_for_agents_template_name: "presentation_for_mairie",
presentation_for_agents_template_name: nil, # C'est la homepage qui joue ce rôle pour ce domaine
address_selection_template_name: nil,
search_banner_template_name: "search/banners/rdv_mairie",
online_reservation_with_public_link: true,
Expand Down
15 changes: 12 additions & 3 deletions app/models/motif.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ def lieux
# Validation
validates :visibility_type, inclusion: { in: VISIBILITY_TYPES }
validates :sectorisation_level, inclusion: { in: SECTORISATION_TYPES }
validates :name, presence: true, uniqueness: { scope: %i[organisation location_type service],
conditions: -> { where(deleted_at: nil) }, }

validates :name, presence: true
validates :color, :default_duration_in_min, :min_public_booking_delay, :max_public_booking_delay, presence: true
validates :min_public_booking_delay, numericality: { greater_than_or_equal_to: 30.minutes, less_than_or_equal_to: 1.year.minutes }
validates :max_public_booking_delay, numericality: { greater_than_or_equal_to: 30.minutes, less_than_or_equal_to: 1.year.minutes }
Expand All @@ -70,6 +68,7 @@ def lieux
validate :not_at_home_if_collectif
validate :cant_change_once_rdvs_exist
validate :cant_be_for_secretariat_and_follow_up
validate :unique_in_org

# Scopes
scope :active, lambda { |active = true|
Expand Down Expand Up @@ -293,4 +292,14 @@ def cant_be_for_secretariat_and_follow_up
errors.add(:for_secretariat, :cant_be_enabled_if_follow_up)
end
end

def unique_in_org
if Motif.active.where.not(id: id).exists?(organisation_id:, name:, service_id:, location_type:)
errors.add(
:base,
:duplicate_detected,
message: %(Il existe déjà dans #{organisation.name} un motif #{human_attribute_value(:location_type)} nommé "#{name}" pour le service #{service.name})
)
end
end
end
72 changes: 72 additions & 0 deletions app/views/admin/territories/motifs/_motifs_table.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/ # locals: (motifs:, display_actions: false, display_checkboxes: false)
table.table.table-centered.table-bordered.table-sm.mb-0[class=(display_checkboxes ? "table-hover" : "")]
thead.thead-light
tr
- if display_checkboxes
th[scope="col"]
input.js-trigger-checkbox[type="checkbox"]

th[scope="col"]
/ Color columns
th[scope="col"]
| Nom
th[scope="col"]
| Service
th[scope="col"]
| Organisation
th[scope="col"]
| Type
th[scope="col"]
| Durée

- if display_actions
th[scope="col"]
tbody
- motifs.each do |motif|
- motif_policy = Agent::MotifPolicy.new(current_agent, motif)
tr
- if display_checkboxes
td
input.js-batch-edit-checkbox[id="edit_checkbox#{motif.id}" type="checkbox" form="batch_edit_form" name="motif_ids[]" value=motif.id]
td
span.badge.badge-pill style="background: #{motif.color};" &nbsp;
td
label.mb-0[for="edit_checkbox#{motif.id}"]
= motif.name
= motif_badges(motif)
td
label.mb-0[for="edit_checkbox#{motif.id}"]
= motif.service.short_name
td
- if motif_policy.show? && display_actions
= link_to(motif.organisation.name, admin_organisation_motifs_path(organisation_id: motif.organisation.id))
- else
= motif.organisation.name
td
label.mb-0[for="edit_checkbox#{motif.id}"]
= Motif.human_attribute_value(:location_type, motif.location_type)
td
label.mb-0[for="edit_checkbox#{motif.id}"]
= "#{motif.default_duration_in_min}"
- if display_actions
td.p-0
.d-flex
- if @current_tab != :archived
= link_to edit_admin_organisation_motif_path(motif.organisation, motif), class: "btn btn-link", title: t("admin.motifs.actions.edit"), target: :_blank do
i.fa.fa-edit
- if motif.archived?
- if motif_policy.unarchive?
= link_to unarchive_admin_territory_motif_path(current_territory, motif), class: "btn btn-link", method: :post, title: t("admin.motifs.actions.unarchive") do
i.fa.fa-arrow-up-from-bracket
- if motif_policy.destroy?
- if motif.destroyable?
= link_to admin_territory_motif_path(current_territory, motif), class: "btn btn-link", method: :delete, title: t("admin.motifs.actions.destroy"), data: { confirm: t("admin.motifs.actions.destroy_confirm") } do
i.fa.fa-trash-alt
- else
button.btn.btn-link [disabled="disabled" title=t("admin.motifs.actions.not_destroyable_explanation")]
i.fa.fa-trash-alt
- else
- if motif_policy.archive?
= link_to archive_admin_territory_motif_path(current_territory, motif), class: "btn btn-link", method: :post, title: t("admin.motifs.actions.archive"), data: { confirm: t("admin.motifs.actions.archive_confirm") } do
i.fa.fa-box-archive
115 changes: 115 additions & 0 deletions app/views/admin/territories/motifs/batch_edit.html.slim
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
= territory_navigation(t("admin.territories.motifs.batch_edit.title"), [link_to(t("admin.territories.motifs.index.title"), admin_territory_motifs_path)])

h1 Modification en masse de #{@motifs.size} motifs

= render partial: "admin/territories/motifs/motifs_table", locals: { motifs: @motifs }

.card.mt-4.mb-4.p-3
= form_with(url: batch_update_admin_territory_motifs_path, method: :post, html: { id: "name_form" }) do |f|
- @motifs.each do |motif|
= f.hidden_field "motif_ids[]", value: motif.id

.form-group
- unique_values = @motifs.map(&:name).tally
= f.label "Nom du motif", for: :name, class: "text-bold"
- if unique_values.size > 1
p Ces motifs ont des noms différents :
ul.mt-0
- unique_values.sort_by(&:last).reverse.each do |value, nb_of_occurrences|
li #{value} (#{nb_of_occurrences})
- else
small.form-text.text-muted.mb-2 Ces #{@motifs.size} motifs ont le même nom
.input-group
= f.text_field :name, value: (unique_values.size > 1 ? "" : unique_values.keys.first), class: "form-control", required: true
.input-group-append
= f.submit "Appliquer à ces #{@motifs.size} motifs", class: "btn btn-primary"

.card.p-3.mb-4
= form_with(url: batch_update_admin_territory_motifs_path, method: :post, html: { id: "service_form" }) do |f|
- @motifs.each do |motif|
= f.hidden_field "motif_ids[]", value: motif.id

.form-group
- unique_values = @motifs.map(&:service).tally
= f.label "Service associé", for: :service_id, class: "text-bold"
- if unique_values.size > 1
p.mb-1 Ces motifs sont associés à des services différents :
ul.mt-0
- unique_values.sort_by(&:last).reverse.each do |value, nb_of_occurrences|
li #{value.name} (#{nb_of_occurrences})
- else
small.form-text.text-muted.mb-2 Ces #{@motifs.size} motifs sont associés au même service
.input-group
= f.select :service_id, current_territory.services.reject(&:secretariat?).map { [_1.name, _1.id] }, { selected: (unique_values.size > 1 ? "" : unique_values.keys.first.id), include_blank: true, required: true }, { class: "custom-select" }
.input-group-append
= f.submit "Appliquer à ces #{@motifs.size} motifs", class: "btn btn-primary"

.card.p-3.mb-4
= form_with(url: batch_update_admin_territory_motifs_path, method: :post, html: { id: "duration_form" }) do |f|
- @motifs.each do |motif|
= f.hidden_field "motif_ids[]", value: motif.id

.form-group
- unique_values = @motifs.map(&:default_duration_in_min).tally
= f.label "Durée par défaut en minutes", for: :default_duration_in_min, class: "text-bold"
- if unique_values.size > 1
p.mb-1 Ces motifs on des durées par défaut différentes :
ul.mt-0
- unique_values.sort_by(&:last).reverse.each do |value, nb_of_occurrences|
li #{value} (#{nb_of_occurrences})
- else
small.form-text.text-muted.mb-2 Ces #{@motifs.size} motifs ont la même durée par défaut
.input-group
= f.number_field :default_duration_in_min, value: (unique_values.keys.size > 1 ? nil : unique_values.keys.first), class: "form-control", required: true
.input-group-append
= f.submit "Appliquer à ces #{@motifs.size} motifs", class: "btn btn-primary"

.card.p-3.mb-4
= form_with(url: batch_update_admin_territory_motifs_path, method: :post, html: { id: "color_form" }) do |f|
- @motifs.each do |motif|
= f.hidden_field "motif_ids[]", value: motif.id

.form-group
- current_colors = @motifs.map(&:color).uniq
/ Cette datalist permet au color picker natif de suggérer les couleurs existantes dans sa palette !
datalist#current_colors
- current_colors.each do |color|
option[value=color]
= f.label "Couleur associée", for: :color, class: "text-bold"
.input-group
= f.color_field :color, value: current_colors.first, class: "form-control", list: "current_colors", required: true
.input-group-append
= f.submit "Appliquer à ces #{@motifs.size} motifs", class: "btn btn-primary"

- %i[restriction_for_rdv instruction_for_rdv custom_cancel_warning_message].each do |instruction_attr_name|
.card.p-3.mb-4
= form_with(url: batch_update_admin_territory_motifs_path, method: :post, html: { id: "#{instruction_attr_name}_form" }) do |f|
- @motifs.each do |motif|
= f.hidden_field "motif_ids[]", value: motif.id

.form-group
- unique_values = @motifs.map(&instruction_attr_name).tally
= f.label t("activerecord.attributes.motif.#{instruction_attr_name}"), for: instruction_attr_name, class: "text-bold"
- if unique_values.size > 1
p.mb-1 Ces motifs ont des textes différents :
ul.mt-0
- unique_values.sort_by(&:last).reverse.each do |value, nb_of_occurrences|
li
- if value.present?
= "#{value}"
- else
em pas de texte
= " (#{nb_of_occurrences})"
- else
small.form-text.text-muted.mb-2 Ces #{@motifs.size} motifs ont le même texte
= f.text_area instruction_attr_name, value: (unique_values.size > 1 ? nil : unique_values.keys.first), class: "form-control"
.mt-1
= link_to image_path("motif_form/#{instruction_attr_name}.png"), target: "_blank" do
span> Voir un exemple
i.fa.fa-external-link-alt>

.float-right
= f.submit "Appliquer à ces #{@motifs.size} motifs", class: "btn btn-primary"

.rdv-text-align-center.mb-4
= link_to("Retour", admin_territory_motifs_path, class: "btn btn-outline-primary")
Loading

0 comments on commit 2a96bd1

Please sign in to comment.