Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BO - signalement] Possibilité d'ajouter des photos et rapport à une visite, et affichage de ces photos, mise à jour des suivis, catégorisation des types de documents #2343

Merged
merged 11 commits into from
Apr 5, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ document.querySelectorAll('.btn-signalement-file-edit').forEach(swbtn => {
+ ' par '+ target.getAttribute('data-partner-name')+target.getAttribute('data-user-name')
document.querySelector('#file-edit-fileid').value = target.getAttribute('data-file-id')

const selectedDocumentType = target.getAttribute('data-documentType');
hmeneuvrier marked this conversation as resolved.
Show resolved Hide resolved
document.querySelector('#fileDescription').value = target.getAttribute('data-description')
document.querySelector('#fr-modal-edit-file-description').classList.remove('fr-hidden')

const documentTypes = JSON.parse(target.getAttribute('data-documentType-list'));
const selectedDocumentType = target.getAttribute('data-documentType');
let typeSelectBox = document.querySelector('#document-type-select');
typeSelectBox.innerHTML = '';
let option = new Option('Sélectionnez un type', '');
Expand Down
199 changes: 148 additions & 51 deletions assets/controllers/back_signalement_view/form_upload_documents.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
const modalUploadFiles = document?.querySelector('#fr-modal-upload-files')
if (modalUploadFiles) {
const dropArea = document.querySelector('.modal-upload-drop-section')
const listContainer = document.querySelector('.modal-upload-list')
const fileSelector = document.querySelector('.modal-upload-files-selector')
const fileSelectorInput = document.querySelector('.modal-upload-files-selector-input')
const addFileRoute = modalUploadFiles.dataset.addFileRoute
const addFileToken = modalUploadFiles.dataset.addFileToken
const waitingSuiviRoute = modalUploadFiles.dataset.waitingSuiviRoute
const deleteTmpFileRoute = modalUploadFiles.dataset.deleteTmpFileRoute
const selectTypeToClone = document.querySelector('#select-type-to-clone')
const selectDesordreToClone = document.querySelector('#select-desordre-to-clone')
const editFileRoute = modalUploadFiles.dataset.editFileRoute
const editFileToken = modalUploadFiles.dataset.editFileToken
const ancre = document.querySelector('#modal-upload-file-dynamic-content')
const btnValidate = document.querySelector('#btn-validate-modal-upload-files')
initializeUploadModal(
'#fr-modal-upload-files',
'#select-type-situation-to-clone',
'#select-type-procedure-to-clone',
'#select-desordre-to-clone',
);

document?.querySelectorAll('.fr-modal-visites-upload-files')?.forEach(modalVisiteUpload => {
initializeUploadModal(
hmeneuvrier marked this conversation as resolved.
Show resolved Hide resolved
'#'+modalVisiteUpload.id,
null,
null,
null,
)
})


function initializeUploadModal(
modalSelector,
selectTypeSituationToCloneSelector,
selectTypeProcedureToCloneSelector,
selectDesordreToCloneSelector,
) {
const modal = document?.querySelector(modalSelector);
if (!modal) return;

const dropArea = modal.querySelector('.modal-upload-drop-section');
const listContainer = modal.querySelector('.modal-upload-list');
const fileSelector = modal.querySelector('.modal-upload-files-selector')
const fileSelectorInput = modal.querySelector('.modal-upload-files-selector-input')
const addFileRoute = modal.dataset.addFileRoute;
const addFileToken = modal.dataset.addFileToken;
const waitingSuiviRoute = modal.dataset.waitingSuiviRoute;
const deleteTmpFileRoute = modal.dataset.deleteTmpFileRoute;
const selectTypeSituationToClone = selectTypeSituationToCloneSelector ? modal.querySelector(selectTypeSituationToCloneSelector) : null;
const selectTypeProcedureToClone = selectTypeProcedureToCloneSelector ? modal.querySelector(selectTypeProcedureToCloneSelector) : null;
const selectDesordreToClone = selectDesordreToCloneSelector ? modal.querySelector(selectDesordreToCloneSelector) : null;
const editFileRoute = modal.dataset.editFileRoute;
const editFileToken = modal.dataset.editFileToken;
const btnValidate = modal.querySelector('#btn-validate-modal-upload-files');
const ancre = modal.querySelector('#modal-upload-file-dynamic-content');


fileSelector.onclick = () => fileSelectorInput.click()
fileSelectorInput.onchange = () => {
Expand Down Expand Up @@ -81,7 +107,7 @@ if (modalUploadFiles) {
listContainer.prepend(div)
let http = new XMLHttpRequest()
let data = new FormData()
if (modalUploadFiles.dataset.fileType == 'photo') {
if (modal.dataset.fileType == 'photo') {
data.append('signalement-add-file[photos][]', file)
} else {
data.append('signalement-add-file[documents][]', file)
Expand All @@ -98,20 +124,35 @@ if (modalUploadFiles) {
let btnDeleteTmpFile = div.querySelector('a.delete-tmp-file')
addEventListenerDeleteTmpFile(btnDeleteTmpFile)
if (this.status == 200) {
modalUploadFiles.dataset.hasChanges = true
if (modalUploadFiles.dataset.fileType == 'photo') {
var clone = selectDesordreToClone.cloneNode(true)
clone.id = 'select-desordre-' + response.response
} else {
var clone = selectTypeToClone.cloneNode(true)
clone.id = 'select-type-' + response.response
}
clone.dataset.fileId = response.response
if (clone.querySelectorAll('option').length == 1) {
clone.remove()
modal.dataset.hasChanges = true
if (null !== selectDesordreToClone || null !== selectTypeSituationToClone || null !== selectTypeProcedureToClone){
let clone
if (modal.dataset.fileType == 'photo') {
clone = selectDesordreToClone.cloneNode(true)
clone.id = 'select-desordre-' + response.response
}else{
if ('situation' === modal.dataset.fileFilter ){
clone = selectTypeSituationToClone.cloneNode(true)
} else {
clone = selectTypeProcedureToClone.cloneNode(true)
}
clone.id = 'select-type-' + response.response
}
clone.dataset.fileId = response.response
if (clone.querySelectorAll('option').length == 1) {
clone.remove()
} else {
div.querySelector('.select-container').appendChild(clone)
addEventListenerSelectTypeDesordre(clone)
}
} else {
div.querySelector('.select-container').appendChild(clone)
addEventListenerSelectTypeDesordre(clone)
if (null !== modal.dataset.documentType){
let divFileId = div.querySelector('#file-id')
divFileId.value = response.response
callEditFileRoute(div)
addEventListenerDescription(div)
}

}
btnDeleteTmpFile.href = btnDeleteTmpFile.href.replace('REPLACE', response.response)
btnDeleteTmpFile.classList.remove('fr-hidden', 'delete-html')
Expand All @@ -127,8 +168,8 @@ if (modalUploadFiles) {
}

function initInnerHtml(file) {
var innerHTML =`<div class="fr-col-12 file-error"></div>`;
if (modalUploadFiles.dataset.fileType == 'photo') {
let innerHTML =`<div class="fr-col-12 file-error"></div>`;
if (modal.dataset.fileType == 'photo') {
innerHTML += `
<div class="fr-col-2">
<img class="fr-content-media__img" src="${URL.createObjectURL(file)}">
Expand All @@ -149,9 +190,21 @@ if (modalUploadFiles) {
<span>0%</span>
</div>
</div>
<div class="fr-col-3 select-container">
</div>
<div class="fr-col-1">
`
if (modal.dataset.documentType == 'PHOTO_VISITE') {
innerHTML += `
<div class="fr-col-3">
<input type="text" id="file-description" name="file[description]"
required="required" class="fr-input" placeholder="Description de l'image">
<input type="hidden" id="file-id" name="file[id]">
</div>
Copy link
Collaborator Author

@hmeneuvrier hmeneuvrier Mar 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ce champ de description n'est pas très pratique, mais je ne vois pas comment faire mieux sachant que chaque fichier ajouté est sur une fr-grid-row.
Des idées ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Qu'entends tu par "pas très pratique" ? (j'ai pas encore testé)

Copy link
Collaborator

@numew numew Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Éventuellement le mettre sous chaque bloc sur toue la longueur (comme le file-error) mais pas sur que ce soit mieux, moi ca me va en l'état.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

`
} else{
innerHTML += `<div class="fr-col-3 select-container">
</div>
<input type="hidden" id="file-id" name="file[id]">`
}
innerHTML += `<div class="fr-col-1">
<a href="${deleteTmpFileRoute}" title="Supprimer" class="fr-btn fr-btn--sm fr-btn--secondary fr-background--white fr-fi-delete-line fr-hidden delete-tmp-file delete-html"></a>
</div>
`
Expand All @@ -164,7 +217,7 @@ if (modalUploadFiles) {
let http = new XMLHttpRequest()
let data = new FormData()
data.append('file_id', selectField.dataset.fileId)
if (modalUploadFiles.dataset.fileType == 'photo') {
if (modal.dataset.fileType == 'photo') {
data.append('documentType', 'PHOTO_SITUATION')
data.append('desordreSlug', selectField.value)
} else {
Expand Down Expand Up @@ -206,9 +259,41 @@ if (modalUploadFiles) {
})
}

function callEditFileRoute(divFileItem) {
let httpEdit = new XMLHttpRequest()
let dataEdit = new FormData()
dataEdit.append('file_id', divFileItem.querySelector('#file-id')?.value)
dataEdit.append('documentType', modal.dataset.documentType)
dataEdit.append('interventionId', modal.dataset.interventionId)
dataEdit.append('description', divFileItem.querySelector('#file-description')?.value)

dataEdit.append('_token', editFileToken)
httpEdit.onreadystatechange = function () {
if (this.readyState == XMLHttpRequest.DONE) {
let response = JSON.parse(this.response)
if (this.status != 200) {
let parent = divFileItem
parent.querySelector('.file-error').innerHTML = '<div class="fr-alert fr-alert--error fr-alert--sm">' + response.response + '</div>'

}
}
}
httpEdit.open('POST', editFileRoute, true)
httpEdit.setRequestHeader("X-Requested-With", "XMLHttpRequest")
httpEdit.send(dataEdit)
}

function addEventListenerDescription(divFileItem) {
listContainer?.querySelectorAll('.fr-grid-row')?.forEach(divFileItem => {
divFileItem.querySelector('#file-description')?.addEventListener('change', (e) => {
callEditFileRoute(divFileItem)
})
})
}

modalUploadFiles.addEventListener('dsfr.conceal', (e) => {
if (modalUploadFiles.dataset.validated == "true" && modalUploadFiles.dataset.hasChanges == "true") {

modal.addEventListener('dsfr.conceal', (e) => {
if (modal.dataset.validated == "true" && modal.dataset.hasChanges == "true") {
fetch(waitingSuiviRoute).then((response) => {
window.location.reload()
})
Expand All @@ -217,37 +302,49 @@ if (modalUploadFiles) {
document.querySelectorAll('a.delete-tmp-file').forEach((button) => {
button.click()
})
modalUploadFiles.dataset.hasChanges = false
modal.dataset.hasChanges = false
})

let fileType
let fileType, fileFilter, documentType, interventionId
document.querySelectorAll('.open-modal-upload-files-btn').forEach((button) => {
button.addEventListener('click', (e) => {
fileType = e.target.dataset.fileType
fileFilter = e.target.dataset.fileFilter ?? null
documentType = e.target.dataset.documentType ?? null
interventionId = e.target.dataset.interventionId ?? null
})
})

modalUploadFiles.addEventListener('dsfr.disclose', (e) => {
modal.addEventListener('dsfr.disclose', (e) => {
listContainer.innerHTML = '';
modalUploadFiles.dataset.validated = false
modalUploadFiles.dataset.hasChanges = false
modalUploadFiles.querySelectorAll('.type-conditional').forEach((type) => {
modal.dataset.validated = false
modal.dataset.hasChanges = false
modal.querySelectorAll('.type-conditional').forEach((type) => {
type.classList.add('fr-hidden')
})
modal.querySelectorAll('.filter-conditional').forEach((type) => {
type.classList.add('fr-hidden')
})
modal.dataset.documentType = documentType
modal.dataset.fileFilter = fileFilter
modal.dataset.interventionId = interventionId
if (fileType == 'photo') {
modalUploadFiles.dataset.fileType = 'photo'
modalUploadFiles.querySelector('.type-photo').classList.remove('fr-hidden')
modal.dataset.fileType = 'photo'
modal.querySelector('.type-photo').classList.remove('fr-hidden')
fileSelectorInput.setAttribute('accept', 'image/*')
} else {
modalUploadFiles.dataset.fileType = 'document'
modalUploadFiles.querySelector('.type-document').classList.remove('fr-hidden')
modal.dataset.fileType = 'document'
modal.querySelector('.type-document').classList.remove('fr-hidden')
fileSelectorInput.setAttribute('accept', '*/*')
}
if (fileFilter == 'procedure') {
modal.querySelector('.filter-procedure').classList.remove('fr-hidden')
} else if (fileFilter == 'situation') {
modal.querySelector('.filter-situation').classList.remove('fr-hidden')
}
})

btnValidate.addEventListener('click', (e) => {
modalUploadFiles.dataset.validated = true
modal.dataset.validated = true
})


}
31 changes: 31 additions & 0 deletions migrations/Version20240329143924.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use App\Entity\File;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20240329143924 extends AbstractMigration
{
public function getDescription(): string
{
return 'Set file_type document for document_type SITUATION_FOYER_BAIL, SITUATION_FOYER_ETAT_DES_LIEUX or SITUATION_FOYER_DPE ';
}

public function up(Schema $schema): void
{
$this->addSql('UPDATE file SET file_type = \''.File::FILE_TYPE_DOCUMENT
.'\' WHERE document_type = \'SITUATION_FOYER_BAIL\' AND file_type = \''.File::FILE_TYPE_PHOTO.'\'');
$this->addSql('UPDATE file SET file_type = \''.File::FILE_TYPE_DOCUMENT
.'\' WHERE document_type = \'SITUATION_FOYER_ETAT_DES_LIEUX\' AND file_type = \''.File::FILE_TYPE_PHOTO.'\'');
$this->addSql('UPDATE file SET file_type = \''.File::FILE_TYPE_DOCUMENT
.'\' WHERE document_type = \'SITUATION_FOYER_DPE\' AND file_type = \''.File::FILE_TYPE_PHOTO.'\'');
}

public function down(Schema $schema): void
{
}
}
4 changes: 4 additions & 0 deletions src/Controller/Back/SignalementController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use App\Repository\SignalementQualificationRepository;
use App\Repository\TagRepository;
use App\Security\Voter\UserVoter;
use App\Service\Signalement\PhotoHelper;
use App\Service\Signalement\SignalementDesordresProcessor;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
Expand Down Expand Up @@ -178,6 +179,8 @@ public function viewSignalement(

$partnerVisite = $affectationRepository->findAffectationWithQualification(Qualification::VISITES, $signalement);

$allPhotosOrdered = PhotoHelper::getSortedPhotos($signalement);

return $this->render('back/signalement/view.html.twig', [
'title' => 'Signalement',
'createdFromDraft' => $signalement->getCreatedFrom(),
Expand Down Expand Up @@ -206,6 +209,7 @@ public function viewSignalement(
'partnersCanVisite' => $partnerVisite,
'pendingVisites' => $interventionRepository->getPendingVisitesForSignalement($signalement),
'isDocumentsEnabled' => $parameterBag->get('feature_documents_enable'),
'allPhotosOrdered' => $allPhotosOrdered,
]);
}

Expand Down
20 changes: 17 additions & 3 deletions src/Controller/Back/SignalementFileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use App\Manager\SuiviManager;
use App\Messenger\Message\PdfExportMessage;
use App\Repository\FileRepository;
use App\Repository\InterventionRepository;
use App\Service\Signalement\SignalementFileProcessor;
use App\Service\UploadHandlerService;
use Doctrine\ORM\EntityManagerInterface;
Expand Down Expand Up @@ -166,12 +167,13 @@ public function deleteFileSignalement(
$suivi = $suiviFactory->createInstanceFrom($this->getUser(), $signalement);
/** @var User $user */
$user = $this->getUser();
$description = $user->getNomComplet().' a supprimé le document suivant :';
$description = $user->getNomComplet().' a supprimé ';
$description .= File::FILE_TYPE_DOCUMENT === $type ? 'le document suivant :' : 'la photo suivante :';
$suivi->setDescription(
$description
.'<ul>'
.'<ul><li>'
.$filename
.'</ul>'
.'</li></ul>'
);
$suivi->setType(SUIVI::TYPE_AUTO);

Expand Down Expand Up @@ -214,6 +216,7 @@ public function editFileSignalement(
Request $request,
FileRepository $fileRepository,
EntityManagerInterface $entityManager,
InterventionRepository $interventionRepository,
): Response {
if (!$this->isCsrfTokenValid('signalement_edit_file_'.$signalement->getId(), $request->get('_token'))) {
if ($request->isXmlHttpRequest()) {
Expand Down Expand Up @@ -250,6 +253,17 @@ public function editFileSignalement(
$file->setDocumentType($documentType);
$desordreSlug = $request->get('desordreSlug');
$file->setDesordreSlug($desordreSlug);
$interventionId = $request->get('interventionId');
if (null !== $interventionId) {
$intervention = $interventionRepository->find($interventionId);
if ($intervention?->getSignalement() === $file->getSignalement()) {
$file->setIntervention($intervention);
}
}
$description = $request->get('description');
hmeneuvrier marked this conversation as resolved.
Show resolved Hide resolved
if (null !== $description) {
$file->setDescription($description);
}
$entityManager->persist($file);
$entityManager->flush();
if ($request->isXmlHttpRequest()) {
Expand Down
Loading
Loading