feat(piece_justificative): replace with multiple attachments for dossier en_construction
This commit is contained in:
parent
4049240c2c
commit
013a05f24a
8 changed files with 147 additions and 20 deletions
|
@ -25,7 +25,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
.attachment-multiple:not(.fr-downloads-group) {
|
||||
.attachment-multiple:not(.fr-downloads-group),
|
||||
.attachment-multiple.fr-downloads-group[data-controller=replace-attachment] {
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-inline-start: 0;
|
||||
|
@ -42,6 +43,10 @@
|
|||
position: relative;
|
||||
top: -1rem;
|
||||
}
|
||||
|
||||
.fr-btn {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.attachment-multiple.fr-downloads-group.destroyable {
|
||||
|
|
|
@ -111,13 +111,14 @@ class Attachment::EditComponent < ApplicationComponent
|
|||
{
|
||||
type: 'button',
|
||||
data: {
|
||||
action: "click->replace-attachment#open"
|
||||
}
|
||||
action: "click->replace-attachment#open",
|
||||
auto_attach_url: auto_attach_url
|
||||
}.compact
|
||||
}
|
||||
end
|
||||
|
||||
def replace_controller_attributes
|
||||
return {} unless user_can_replace? || as_multiple?
|
||||
return {} if !persisted? || !user_can_replace? || as_multiple?
|
||||
|
||||
{
|
||||
"data-controller": 'replace-attachment'
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
- elsif user_can_replace?
|
||||
= button_tag t('.replace'), **replace_button_options, class: "fr-btn fr-btn--tertiary fr-btn--sm", title: "Remplacer le fichier #{attachment.filename}"
|
||||
|
||||
.fr-py-1v
|
||||
- if downloadable?
|
||||
= render Dsfr::DownloadComponent.new(attachment:)
|
||||
- else
|
||||
- if downloadable?
|
||||
= render Dsfr::DownloadComponent.new(attachment:)
|
||||
- else
|
||||
.fr-py-1v
|
||||
%span.attachment-filename.fr-mr-1w= link_to_if(viewable?, attachment.filename.to_s, helpers.url_for(attachment.blob), title: "Ouvrir le fichier #{attachment.filename.to_s}", **helpers.external_link_attributes)
|
||||
|
||||
= render Attachment::ProgressComponent.new(attachment: attachment)
|
||||
|
|
|
@ -12,15 +12,18 @@ class Attachment::MultipleComponent < ApplicationComponent
|
|||
attr_reader :view_as
|
||||
attr_reader :user_can_destroy
|
||||
alias user_can_destroy? user_can_destroy
|
||||
attr_reader :user_can_replace
|
||||
alias user_can_replace? user_can_replace
|
||||
|
||||
delegate :count, :empty?, to: :attachments, prefix: true
|
||||
|
||||
def initialize(champ:, attached_file:, form_object_name: nil, view_as: :link, user_can_destroy: true, max: nil)
|
||||
def initialize(champ:, attached_file:, form_object_name: nil, view_as: :link, user_can_destroy: true, user_can_replace: false, max: nil)
|
||||
@champ = champ
|
||||
@attached_file = attached_file
|
||||
@form_object_name = form_object_name
|
||||
@view_as = view_as
|
||||
@user_can_destroy = user_can_destroy
|
||||
@user_can_replace = user_can_replace
|
||||
@max = max || DEFAULT_MAX_ATTACHMENTS
|
||||
|
||||
@attachments = attached_file.attachments || []
|
||||
|
@ -48,4 +51,12 @@ class Attachment::MultipleComponent < ApplicationComponent
|
|||
|
||||
nil
|
||||
end
|
||||
|
||||
def replace_controller_attributes
|
||||
return {} unless user_can_replace?
|
||||
|
||||
{
|
||||
"data-controller": 'replace-attachment'
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
.fr-mb-4w.attachment-multiple{ class: class_names("fr-downloads-group": view_as == :download, "destroyable": user_can_destroy?) }
|
||||
.fr-mb-4w.attachment-multiple{ class: class_names("fr-downloads-group": view_as == :download, "destroyable": user_can_destroy?), **replace_controller_attributes }
|
||||
= template
|
||||
|
||||
%ul
|
||||
- each_attachment do |attachment, index|
|
||||
%li{ id: dom_id(attachment) }
|
||||
= render Attachment::EditComponent.new(champ:, attached_file:, attachment:, index:, as_multiple: true, view_as:, user_can_destroy:, form_object_name:)
|
||||
= render Attachment::EditComponent.new(champ:, attached_file:, attachment:, index:, as_multiple: true, view_as:, user_can_destroy:, user_can_replace:, form_object_name:)
|
||||
|
||||
%div{ id: empty_component_id, class: class_names("hidden": !can_attach_next?) }
|
||||
= render Attachment::EditComponent.new(champ:, attached_file:, attachment: nil, index: attachments_count, user_can_destroy:, form_object_name:)
|
||||
= render Attachment::EditComponent.new(champ:, attached_file:, attachment: nil, index: attachments_count, user_can_destroy:, user_can_replace:, form_object_name:)
|
||||
|
||||
// single poll and refresh message for all attachments
|
||||
= render Attachment::PendingPollComponent.new(attachments: attachments, poll_url:, context: poll_context)
|
||||
|
|
|
@ -6,8 +6,25 @@ export class ReplaceAttachmentController extends ApplicationController {
|
|||
|
||||
declare readonly inputTarget: HTMLInputElement;
|
||||
|
||||
open() {
|
||||
open(event: Event) {
|
||||
show(this.inputTarget);
|
||||
this.inputTarget.click(); // opens input prompt
|
||||
|
||||
const target = event.currentTarget as HTMLButtonElement;
|
||||
|
||||
if (target.dataset.autoAttachUrl) {
|
||||
// set the auto attach url specific to this button to replace the related attachment
|
||||
this.inputTarget.dataset.originalAutoAttachUrl =
|
||||
this.inputTarget.dataset.autoAttachUrl;
|
||||
|
||||
this.inputTarget.dataset.autoAttachUrl = target.dataset.autoAttachUrl;
|
||||
|
||||
// reset autoAttachUrl which would add an attachment
|
||||
// when replace is not finalized
|
||||
this.inputTarget.addEventListener('cancel', () => {
|
||||
this.inputTarget.dataset.autoAttachUrl =
|
||||
this.inputTarget.dataset.originalAutoAttachUrl;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,13 +38,7 @@ RSpec.describe Attachment::MultipleComponent, type: :component do
|
|||
|
||||
context 'when there is an attachment' do
|
||||
before do
|
||||
attached_file.attach(
|
||||
io: StringIO.new("x" * 2),
|
||||
filename: "me.jpg",
|
||||
content_type: "image/jpeg",
|
||||
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
||||
)
|
||||
champ.save!
|
||||
attach_to_champ(attached_file, champ)
|
||||
end
|
||||
|
||||
it 'renders the filenames' do
|
||||
|
@ -98,4 +92,26 @@ RSpec.describe Attachment::MultipleComponent, type: :component do
|
|||
expect(subject).to have_selector('[data-controller=turbo-poll]')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user can replace' do
|
||||
let(:kwargs) { { user_can_replace: true } }
|
||||
|
||||
before do
|
||||
attach_to_champ(attached_file, champ)
|
||||
end
|
||||
|
||||
it 'setup controller' do
|
||||
expect(subject).to have_selector('[data-controller=replace-attachment]').once
|
||||
end
|
||||
end
|
||||
|
||||
def attach_to_champ(attached_file, champ)
|
||||
attached_file.attach(
|
||||
io: StringIO.new("x" * 2),
|
||||
filename: "me.jpg",
|
||||
content_type: "image/jpeg",
|
||||
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
||||
)
|
||||
champ.save!
|
||||
end
|
||||
end
|
||||
|
|
77
spec/system/users/en_construction_spec.rb
Normal file
77
spec/system/users/en_construction_spec.rb
Normal file
|
@ -0,0 +1,77 @@
|
|||
describe "Dossier en_construction" do
|
||||
let(:user) { create(:user) }
|
||||
let(:procedure) { create(:simple_procedure, :with_piece_justificative, :with_titre_identite) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_individual, :with_populated_champs, user:, procedure:) }
|
||||
|
||||
let(:tdc) {
|
||||
procedure.active_revision.types_de_champ_public.find { _1.type_champ == "piece_justificative" }
|
||||
}
|
||||
|
||||
let(:champ) {
|
||||
dossier.champs_public.find { _1.type_de_champ_id == tdc.id }
|
||||
}
|
||||
|
||||
scenario 'delete a non mandatory piece justificative', js: true do
|
||||
visit_dossier(dossier)
|
||||
|
||||
expect(page).not_to have_button("Remplacer")
|
||||
click_on "Supprimer le fichier toto.txt"
|
||||
|
||||
expect(page).not_to have_text("toto.txt")
|
||||
end
|
||||
|
||||
context "with a mandatory piece justificative" do
|
||||
before do
|
||||
tdc.update_attribute(:mandatory, true)
|
||||
end
|
||||
|
||||
scenario 'remplace a mandatory piece justificative', js: true do
|
||||
visit_dossier(dossier)
|
||||
|
||||
click_on "Remplacer le fichier toto.txt"
|
||||
|
||||
input_selector = "#attachment-multiple-empty-#{champ.id}"
|
||||
expect(page).to have_selector(input_selector)
|
||||
find(input_selector).attach_file(Rails.root.join('spec/fixtures/files/file.pdf'))
|
||||
|
||||
expect(page).to have_text("file.pdf")
|
||||
expect(page).not_to have_text("toto.txt")
|
||||
end
|
||||
end
|
||||
|
||||
context "with a mandatory titre identite" do
|
||||
let(:tdc) {
|
||||
procedure.active_revision.types_de_champ_public.find { _1.type_champ == "titre_identite" }
|
||||
}
|
||||
|
||||
before do
|
||||
tdc.update_attribute(:mandatory, true)
|
||||
end
|
||||
|
||||
scenario 'remplace a mandatory titre identite', js: true do
|
||||
visit_dossier(dossier)
|
||||
|
||||
click_on "Remplacer le fichier toto.png"
|
||||
|
||||
input_selector = ".attachment-input-#{champ.piece_justificative_file.attachments.first.id}"
|
||||
expect(page).to have_selector(input_selector)
|
||||
find(input_selector).attach_file(Rails.root.join('spec/fixtures/files/file.pdf'))
|
||||
|
||||
expect(page).to have_text("file.pdf")
|
||||
expect(page).not_to have_text("toto.png")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def visit_dossier(dossier)
|
||||
visit modifier_dossier_path(dossier)
|
||||
|
||||
expect(page).to have_current_path(new_user_session_path)
|
||||
fill_in 'user_email', with: user.email
|
||||
fill_in 'user_password', with: user.password
|
||||
click_on 'Se connecter'
|
||||
|
||||
expect(page).to have_current_path(modifier_dossier_path(dossier))
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue