feat(gallery): display pieces jointes in a gallery
This commit is contained in:
parent
a30d202971
commit
f02cb19fd4
13 changed files with 179 additions and 0 deletions
BIN
app/assets/images/apercu-indisponible.png
Normal file
BIN
app/assets/images/apercu-indisponible.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.6 KiB |
BIN
app/assets/images/pdf-placeholder.png
Normal file
BIN
app/assets/images/pdf-placeholder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.8 KiB |
66
app/assets/stylesheets/gallery.scss
Normal file
66
app/assets/stylesheets/gallery.scss
Normal file
|
@ -0,0 +1,66 @@
|
|||
.gallery {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.gallery-item {
|
||||
display: block;
|
||||
}
|
||||
|
||||
a {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.champ-libelle {
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fr-icon-eye {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
background-color: var(--background-default-grey);
|
||||
color: var(--text-active-blue-france);
|
||||
border: 1px solid var(--border-active-blue-france);
|
||||
padding: 0.25rem 0.75rem;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--hover-tint);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: var(--active-tint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lg-has-iframe {
|
||||
width: 80% !important;
|
||||
margin-top: 50px;
|
||||
}
|
||||
|
||||
.lg-icon {
|
||||
--hover-tint: none;
|
||||
--active-tint: none;
|
||||
}
|
||||
|
||||
.lg-sub-html {
|
||||
background-image: linear-gradient(180deg, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1)) !important;
|
||||
padding: 30px 40px 0 40px !important;
|
||||
}
|
|
@ -159,6 +159,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
&-eye {
|
||||
&:before,
|
||||
&:after {
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12.0003 3C17.3924 3 21.8784 6.87976 22.8189 12C21.8784 17.1202 17.3924 21 12.0003 21C6.60812 21 2.12215 17.1202 1.18164 12C2.12215 6.87976 6.60812 3 12.0003 3ZM12.0003 19C16.2359 19 19.8603 16.052 20.7777 12C19.8603 7.94803 16.2359 5 12.0003 5C7.7646 5 4.14022 7.94803 3.22278 12C4.14022 16.052 7.7646 19 12.0003 19ZM12.0003 16.5C9.51498 16.5 7.50026 14.4853 7.50026 12C7.50026 9.51472 9.51498 7.5 12.0003 7.5C14.4855 7.5 16.5003 9.51472 16.5003 12C16.5003 14.4853 14.4855 16.5 12.0003 16.5ZM12.0003 14.5C13.381 14.5 14.5003 13.3807 14.5003 12C14.5003 10.6193 13.381 9.5 12.0003 9.5C10.6196 9.5 9.50026 10.6193 9.50026 12C9.50026 13.3807 10.6196 14.5 12.0003 14.5Z'%3E%3C/path%3E%3C/svg%3E");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='currentColor'%3E%3Cpath d='M12.0003 3C17.3924 3 21.8784 6.87976 22.8189 12C21.8784 17.1202 17.3924 21 12.0003 21C6.60812 21 2.12215 17.1202 1.18164 12C2.12215 6.87976 6.60812 3 12.0003 3ZM12.0003 19C16.2359 19 19.8603 16.052 20.7777 12C19.8603 7.94803 16.2359 5 12.0003 5C7.7646 5 4.14022 7.94803 3.22278 12C4.14022 16.052 7.7646 19 12.0003 19ZM12.0003 16.5C9.51498 16.5 7.50026 14.4853 7.50026 12C7.50026 9.51472 9.51498 7.5 12.0003 7.5C14.4855 7.5 16.5003 9.51472 16.5003 12C16.5003 14.4853 14.4855 16.5 12.0003 16.5ZM12.0003 14.5C13.381 14.5 14.5003 13.3807 14.5003 12C14.5003 10.6193 13.381 9.5 12.0003 9.5C10.6196 9.5 9.50026 10.6193 9.50026 12C9.50026 13.3807 10.6196 14.5 12.0003 14.5Z'%3E%3C/path%3E%3C/svg%3E");
|
||||
}
|
||||
}
|
||||
|
||||
&-file-copy-line {
|
||||
&:before,
|
||||
&:after {
|
||||
|
|
|
@ -358,6 +358,13 @@ module Instructeurs
|
|||
redirect_to instructeur_procedure_path(procedure)
|
||||
end
|
||||
|
||||
def pieces_jointes
|
||||
@dossier = current_instructeur.dossiers.find(params[:dossier_id])
|
||||
@champs_with_pieces_jointes = @dossier
|
||||
.champs
|
||||
.filter { _1.class.in?([Champs::PieceJustificativeChamp, Champs::TitreIdentiteChamp]) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dossier_scope
|
||||
|
|
36
app/javascript/controllers/lightbox_controller.ts
Normal file
36
app/javascript/controllers/lightbox_controller.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { Controller } from '@hotwired/stimulus';
|
||||
import lightGallery from 'lightgallery';
|
||||
import { LightGallery } from 'lightgallery/lightgallery';
|
||||
import lgThumbnail from 'lightgallery/plugins/thumbnail';
|
||||
import lgZoom from 'lightgallery/plugins/zoom';
|
||||
import lgRotate from 'lightgallery/plugins/rotate';
|
||||
import 'lightgallery/css/lightgallery-bundle.css';
|
||||
|
||||
export default class extends Controller {
|
||||
lightGallery?: LightGallery;
|
||||
|
||||
connect(): void {
|
||||
const options = {
|
||||
plugins: [lgZoom, lgThumbnail, lgRotate],
|
||||
flipVertical: false,
|
||||
flipHorizontal: false,
|
||||
animateThumb: false,
|
||||
zoomFromOrigin: false,
|
||||
allowMediaOverlap: true,
|
||||
toggleThumb: true,
|
||||
selector: '.gallery-link'
|
||||
};
|
||||
|
||||
this.lightGallery = lightGallery(this.element as HTMLElement, options);
|
||||
|
||||
const downloadIcon = document.querySelector('.lg-download');
|
||||
|
||||
if (downloadIcon != null) {
|
||||
downloadIcon.removeAttribute('target');
|
||||
}
|
||||
}
|
||||
|
||||
disconnect(): void {
|
||||
this.lightGallery?.destroy();
|
||||
}
|
||||
}
|
|
@ -7,6 +7,10 @@
|
|||
instructeur_dossier_path(dossier.procedure, dossier),
|
||||
notification: notifications_summary[:demande])
|
||||
|
||||
- if dossier.revision.types_de_champ.any?(&:piece_justificative?)
|
||||
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.attachments'),
|
||||
pieces_jointes_instructeur_dossier_path(dossier.procedure, dossier))
|
||||
|
||||
= dynamic_tab_item(t('views.instructeurs.dossiers.tab_steps.private_annotations'),
|
||||
annotations_privees_instructeur_dossier_path(dossier.procedure, dossier),
|
||||
notification: notifications_summary[:annotations_privees])
|
||||
|
|
29
app/views/instructeurs/dossiers/pieces_jointes.html.haml
Normal file
29
app/views/instructeurs/dossiers/pieces_jointes.html.haml
Normal file
|
@ -0,0 +1,29 @@
|
|||
- content_for(:title, "Pièces jointes")
|
||||
|
||||
= render partial: "header", locals: { dossier: @dossier }
|
||||
|
||||
.fr-container
|
||||
.gallery{ "data-controller": "lightbox"}
|
||||
- @champs_with_pieces_jointes.each do |champ|
|
||||
- champ.piece_justificative_file.each do |attachment|
|
||||
.gallery-item
|
||||
- blob = attachment.blob
|
||||
- if blob.content_type == 'application/pdf'
|
||||
= link_to blob.url, id: blob.id, data: { iframe: true, src: blob.url }, class: 'gallery-link', type: "application/pdf", title: "#{champ.libelle} -- #{blob.filename}" do
|
||||
.thumbnail
|
||||
= image_tag("pdf-placeholder.png")
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
Visualiser
|
||||
.champ-libelle
|
||||
= champ.libelle
|
||||
= render Attachment::ShowComponent.new(attachment: attachment, new_tab: true)
|
||||
|
||||
- else
|
||||
= link_to image_url(blob.url), title: "#{champ.libelle} -- #{blob.filename}", data: { src: blob.url }, class: 'gallery-link' do
|
||||
.thumbnail
|
||||
= image_tag(blob.url)
|
||||
.fr-btn.fr-btn--tertiary.fr-btn--icon-left.fr-icon-eye{ role: :button }
|
||||
Visualiser
|
||||
.champ-libelle
|
||||
= champ.libelle
|
||||
= render Attachment::ShowComponent.new(attachment: attachment, new_tab: true)
|
|
@ -379,6 +379,7 @@ en:
|
|||
messaging: Messaging
|
||||
involved_persons: Involved persons
|
||||
reaffectation: reassignment
|
||||
attachments: Attachments
|
||||
tab_explainations:
|
||||
a_suivre: No instructor is assigned to follow up on these files. Be the first !
|
||||
suivis: The folders that are in this tab are only those that you follow. You can exchange with the requester until you can accept them, refuse them or classify them without follow-up.
|
||||
|
|
|
@ -382,6 +382,7 @@ fr:
|
|||
messaging: Messagerie
|
||||
involved_persons: Personnes impliquées
|
||||
reaffectation: Réaffectation
|
||||
attachments: Pièces jointes
|
||||
tab_explainations:
|
||||
a_suivre: Aucun instructeur n’est affecté au suivi de ces dossiers. Soyez le premier !
|
||||
suivis: Les dossiers qui sont dans cet onglet sont uniquement ceux que vous suivez. Vous pouvez échanger avec le demandeur jusqu’à pouvoir les accepter, les refuser ou les classer sans suite.
|
||||
|
|
|
@ -502,6 +502,7 @@ Rails.application.routes.draw do
|
|||
get 'print' => 'dossiers#print'
|
||||
get 'telecharger_pjs' => 'dossiers#telecharger_pjs'
|
||||
get 'reaffectation'
|
||||
get 'pieces_jointes'
|
||||
post 'reaffecter'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"highcharts": "^10.3.3",
|
||||
"intersection-observer": "^0.12.2",
|
||||
"is-hotkey": "^0.2.0",
|
||||
"lightgallery": "^2.7.2",
|
||||
"maplibre-gl": "^1.15.2",
|
||||
"match-sorter": "^6.3.4",
|
||||
"patch-package": "^7.0.0",
|
||||
|
|
|
@ -1372,4 +1372,29 @@ describe Instructeurs::DossiersController, type: :controller do
|
|||
|
||||
it { expect(subject).to have_http_status(:ok) }
|
||||
end
|
||||
|
||||
describe '#pieces_jointes' do
|
||||
let(:procedure) { create(:procedure, :published, types_de_champ_public: [{ type: :piece_justificative }], instructeurs:) }
|
||||
let(:dossier) { create(:dossier, :en_construction, :with_populated_champs, procedure: procedure) }
|
||||
|
||||
before do
|
||||
dossier.champs.first.piece_justificative_file.attach(
|
||||
io: StringIO.new("image file"),
|
||||
filename: "image.jpeg",
|
||||
content_type: "image/jpeg",
|
||||
# we don't want to run virus scanner on this file
|
||||
metadata: { virus_scan_result: ActiveStorage::VirusScanner::SAFE }
|
||||
)
|
||||
get :pieces_jointes, params: {
|
||||
procedure_id: procedure.id,
|
||||
dossier_id: dossier.id
|
||||
}
|
||||
end
|
||||
|
||||
it do
|
||||
expect(response.body).to include('Télécharger le fichier toto.txt')
|
||||
expect(response.body).to include('Télécharger le fichier image.jpeg')
|
||||
expect(response.body).to include('Visualiser')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue