Skip to content

Commit

Permalink
Merge pull request #3631 from MTES-MCT/feature/3601-page-admin-bailleurs
Browse files Browse the repository at this point in the history
[FO-BO Signalement] Modifier dénomination bailleur
  • Loading branch information
emilschn authored Jan 30, 2025
2 parents f3923ae + b9f043e commit 1c6eef1
Show file tree
Hide file tree
Showing 14 changed files with 562 additions and 1 deletion.
1 change: 1 addition & 0 deletions assets/scripts/app-back-bo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ import './vanilla/controllers/back_signalement_list_export/back_signalement_list
import './vanilla/controllers/back_profil_edit_email/back_profil_edit_email.js';
import './vanilla/controllers/back_zone_view/back_zone_index.js';
import './vanilla/controllers/back_territory_view/back_territory_index.js';
import './vanilla/controllers/back_bailleur_view/back_bailleur_index.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { loadWindowWithLocalStorage, updateLocalStorageWithFormParams } from '../../services/list_filter_helper'

const searchTerritoryForm = document.getElementById('search-bailleur-form')

if (searchTerritoryForm) {
document.querySelectorAll('.open-modal-bailleur-delete').forEach((button) => {
button.addEventListener('click', (e) => {
document.getElementById('fr-modal-bailleur-delete-bailleur-name').textContent = e.target.dataset.name
document.getElementById('fr-modal-bailleur-delete-btn-submit').href = e.target.dataset.url
})
})
updateLocalStorageWithFormParams('search-bailleur-form')
}
loadWindowWithLocalStorage('click', '[data-filter-list-bailleur]', 'search-bailleur-form')
94 changes: 94 additions & 0 deletions src/Controller/Back/BackBailleurController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

namespace App\Controller\Back;

use App\Entity\Bailleur;
use App\Form\BailleurType;
use App\Form\SearchBailleurType;
use App\Repository\BailleurRepository;
use App\Service\ListFilters\SearchBailleur;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/bo/bailleur')]
class BackBailleurController extends AbstractController
{
#[Route('/', name: 'back_bailleur_index', methods: ['GET'])]
#[IsGranted('ROLE_ADMIN')]
public function index(
Request $request,
BailleurRepository $bailleurRepository,
ParameterBagInterface $parameterBag,
): Response {
$searchBailleur = new SearchBailleur();
$form = $this->createForm(SearchBailleurType::class, $searchBailleur);
$form->handleRequest($request);
if ($form->isSubmitted() && !$form->isValid()) {
$searchBailleur = new SearchBailleur();
}
$maxListPagination = $parameterBag->get('standard_max_list_pagination');
$paginatedBailleurs = $bailleurRepository->findFilteredPaginated($searchBailleur, $maxListPagination);

return $this->render('back/bailleur/index.html.twig', [
'form' => $form,
'searchBailleur' => $searchBailleur,
'bailleurs' => $paginatedBailleurs,
'pages' => (int) ceil($paginatedBailleurs->count() / $maxListPagination),
]);
}

#[Route('/editer/{bailleur}', name: 'back_bailleur_edit', methods: ['GET', 'POST'])]
#[IsGranted('ROLE_ADMIN')]
public function edit(
Bailleur $bailleur,
Request $request,
EntityManagerInterface $em,
): Response {
$form = $this->createForm(BailleurType::class, $bailleur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->flush();
$this->addFlash('success', 'Le bailleur a bien été modifié.');

return $this->redirectToRoute('back_bailleur_edit', ['bailleur' => $bailleur->getId()]);
}

return $this->render('back/bailleur/edit.html.twig', [
'form' => $form,
'bailleur' => $bailleur,
]);
}

#[Route('/supprimer/{bailleur}', name: 'back_bailleur_delete', methods: ['GET'])]
#[IsGranted('ROLE_ADMIN')]
public function delete(
Bailleur $bailleur,
Request $request,
EntityManagerInterface $em,
): RedirectResponse {
if (!$this->isCsrfTokenValid('bailleur_delete', $request->query->get('_token'))) {
$this->addFlash('error', 'Le token CSRF est invalide.');

return $this->redirectToRoute('back_bailleur_index');
}
if ($nb = $bailleur->getSignalements()->count() > 0) {
$this->addFlash('error', 'Le bailleur ne peut pas être supprimé car il est lié à '.$nb.' signalements.');

return $this->redirectToRoute('back_bailleur_index');
}
foreach ($bailleur->getBailleurTerritories() as $bailleurTerritory) {
$em->remove($bailleurTerritory);
}
$em->remove($bailleur);
$em->flush();
$this->addFlash('success', 'Le bailleur a bien été supprimé.');

return $this->redirectToRoute('back_bailleur_index');
}
}
10 changes: 9 additions & 1 deletion src/Entity/Bailleur.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Bailleur

#[ORM\OneToMany(mappedBy: 'bailleur', targetEntity: Signalement::class)]
#[Ignore]
private Collection $signalements; // @phpstan-ignore-line
private Collection $signalements;

#[ORM\OneToMany(mappedBy: 'bailleur', targetEntity: Partner::class)]
#[Ignore]
Expand Down Expand Up @@ -135,4 +135,12 @@ public function getPartners(): Collection
{
return $this->partners;
}

/**
* @return Collection<int, Signalement>
*/
public function getSignalements(): Collection
{
return $this->signalements;
}
}
38 changes: 38 additions & 0 deletions src/Form/BailleurType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Form;

use App\Entity\Bailleur;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class BailleurType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', null, [
'label' => 'Nom',
])
->add('submit', SubmitType::class, [
'label' => 'Valider',
'attr' => ['class' => 'fr-btn fr-icon-check-line fr-btn--icon-left'],
'row_attr' => ['class' => 'fr-text--right'],
]);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Bailleur::class,
'csrf_token_id' => 'bailleur_type',
]);
}

public function getBlockPrefix(): string
{
return '';
}
}
60 changes: 60 additions & 0 deletions src/Form/SearchBailleurType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace App\Form;

use App\Entity\Territory;
use App\Service\ListFilters\SearchBailleur;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SearchType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class SearchBailleurType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('queryName', SearchType::class, [
'required' => false,
'label' => 'Bailleur',
'attr' => ['placeholder' => 'Taper le nom ou une partie du nom du bailleur'],
]);
$builder->add('territory', EntityType::class, [
'class' => Territory::class,
'required' => false,
'label' => 'Territoire',
'placeholder' => 'Tous les territoires',
'choice_label' => function (Territory $territory) {
return $territory->getZip().' - '.$territory->getName();
},
]);
$builder->add('orderType', ChoiceType::class, [
'choices' => [
'Ordre alphabétique (A -> Z)' => 'b.name-ASC',
'Ordre alphabétique inversé (Z -> A)' => 'b.name-DESC',
],
'required' => false,
'placeholder' => false,
'label' => 'Trier par',
'data' => 'b.name-ASC',
]);
$builder->add('page', HiddenType::class);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => SearchBailleur::class,
'csrf_protection' => false,
'method' => 'GET',
'attr' => ['id' => 'search-bailleur-form', 'class' => 'fr-p-4v bo-filter-form'],
]);
}

public function getBlockPrefix(): string
{
return '';
}
}
31 changes: 31 additions & 0 deletions src/Repository/BailleurRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use App\Entity\Bailleur;
use App\Entity\Territory;
use App\Entity\User;
use App\Service\ListFilters\SearchBailleur;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\Persistence\ManagerRegistry;

/**
Expand Down Expand Up @@ -114,4 +116,33 @@ public function findBailleursIndexedByName(?bool $raisonSociale = false): array

return $indexed;
}

public function findFilteredPaginated(SearchBailleur $searchBailleur, int $maxResult): Paginator
{
$qb = $this->createQueryBuilder('b');
$qb->select('b', 'bt')
->leftJoin('b.bailleurTerritories', 'bt');

if (!empty($searchBailleur->getOrderType())) {
[$orderField, $orderDirection] = explode('-', $searchBailleur->getOrderType());
$qb->orderBy($orderField, $orderDirection);
} else {
$qb->orderBy('b.name', 'ASC');
}

if ($searchBailleur->getQueryName()) {
$qb->andWhere('LOWER(b.name) LIKE :queryName');
$qb->setParameter('queryName', '%'.strtolower($searchBailleur->getQueryName()).'%');
}
if (null !== $searchBailleur->getTerritory()) {
$qb->innerJoin('b.bailleurTerritories', 'bt_select')
->andWhere('bt_select.territory = :territory')
->setParameter('territory', $searchBailleur->getTerritory());
}

$firstResult = ($searchBailleur->getPage() - 1) * $maxResult;
$qb->setFirstResult($firstResult)->setMaxResults($maxResult);

return new Paginator($qb->getQuery());
}
}
45 changes: 45 additions & 0 deletions src/Service/ListFilters/SearchBailleur.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace App\Service\ListFilters;

use App\Entity\Territory;
use App\Service\Behaviour\SearchQueryTrait;

class SearchBailleur
{
use SearchQueryTrait;

private ?string $queryName = null;
private ?Territory $territory = null;
private ?string $orderType = null;

public function getQueryName(): ?string
{
return $this->queryName;
}

public function setQueryName(?string $queryName): void
{
$this->queryName = $queryName;
}

public function getTerritory(): ?Territory
{
return $this->territory;
}

public function setTerritory(?Territory $territory): void
{
$this->territory = $territory;
}

public function getOrderType(): ?string
{
return $this->orderType;
}

public function setOrderType(?string $orderType): void
{
$this->orderType = $orderType;
}
}
2 changes: 2 additions & 0 deletions src/Service/Menu/MenuBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ public function build(): MenuItem
->addChild(new MenuItem(label: 'Règles d\'auto-affectation', route: 'back_auto_affectation_rule_index', roleGranted: User::ROLE_ADMIN))
->addChild(new MenuItem(label: 'Résumés de suivis', route: 'back_suivi_summaries_index', roleGranted: User::ROLE_ADMIN))
->addChild(new MenuItem(label: 'Territoires', route: 'back_territory_index', roleGranted: User::ROLE_ADMIN))
->addChild(new MenuItem(label: 'Bailleurs', route: 'back_bailleur_index', roleGranted: User::ROLE_ADMIN))
->addChild(new MenuItem(route: 'back_archived_users_reactiver'))
->addChild(new MenuItem(route: 'back_territory_edit'))
->addChild(new MenuItem(route: 'back_bailleur_edit'))
->addChild(new MenuItem(route: 'back_auto_affectation_rule_new'))
->addChild(new MenuItem(route: 'back_auto_affectation_rule_edit'))
;
Expand Down
32 changes: 32 additions & 0 deletions templates/back/bailleur/edit.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{% extends 'back/base_bo.html.twig' %}

{% block title %}Modifier le bailleur {{bailleur.name}}{% endblock %}

{% block content %}
<section class="fr-p-5v">
{% include 'back/breadcrumb_bo.html.twig' with {
'level2Title': 'Outils SA',
'level2Link': '',
'level2Label': '',
'level3Title': 'Bailleurs',
'level3Link': path('back_bailleur_index'),
'level4Title': 'Modifier le bailleur '~ bailleur.name,
'level4Link': '',
} %}
<header>
<div class="fr-grid-row">
<div class="fr-col-12 fr-text--left">
<h1 class="fr-mb-0">Bailleur {{bailleur.name}}</h1>
</div>
</div>
</header>
</section>

<section class="fr-grid-row fr-px-5v fr-pb-5v">
<div class="fr-col-12">
{% form_theme form 'form/dsfr_theme.html.twig' %}
{{form(form, {'attr': {'id': 'form-edit-bailleur'}} )}}
</div>
</section>

{% endblock %}
Loading

0 comments on commit 1c6eef1

Please sign in to comment.