mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch '367-page-mes-parcours' into 'master'
Resolve "Reorganise page 'Mes parcours'" Closes #367 See merge request Chill-Projet/chill-bundles!807
This commit is contained in:
commit
713c50309b
@ -11,46 +11,97 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Chill\PersonBundle\Controller;
|
namespace Chill\PersonBundle\Controller;
|
||||||
|
|
||||||
|
use Chill\MainBundle\Entity\User;
|
||||||
use Chill\MainBundle\Pagination\PaginatorFactory;
|
use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||||
|
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactoryInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
|
use Chill\PersonBundle\Repository\AccompanyingPeriodACLAwareRepository;
|
||||||
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
|
use Chill\PersonBundle\Repository\AccompanyingPeriodRepository;
|
||||||
|
use Doctrine\ORM\NonUniqueResultException;
|
||||||
|
use Doctrine\ORM\NoResultException;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
class UserAccompanyingPeriodController extends AbstractController
|
class UserAccompanyingPeriodController extends AbstractController
|
||||||
{
|
{
|
||||||
public function __construct(private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository, private readonly PaginatorFactory $paginatorFactory) {}
|
public function __construct(
|
||||||
|
private readonly AccompanyingPeriodRepository $accompanyingPeriodRepository,
|
||||||
|
private readonly PaginatorFactory $paginatorFactory,
|
||||||
|
private readonly AccompanyingPeriodACLAwareRepository $accompanyingPeriodACLAwareRepository,
|
||||||
|
private readonly FilterOrderHelperFactoryInterface $filterOrderHelperFactory,
|
||||||
|
private readonly TranslatorInterface $translator,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NonUniqueResultException
|
||||||
|
* @throws NoResultException
|
||||||
|
*/
|
||||||
#[Route(path: '/{_locale}/person/accompanying-periods/my', name: 'chill_person_accompanying_period_user')]
|
#[Route(path: '/{_locale}/person/accompanying-periods/my', name: 'chill_person_accompanying_period_user')]
|
||||||
public function listAction(Request $request): Response
|
public function listAction(Request $request): Response
|
||||||
{
|
{
|
||||||
$active = $request->query->getBoolean('active', true);
|
$filter = (int) $request->query->get('filter', 2);
|
||||||
$steps = match ($active) {
|
$user = $this->getUser();
|
||||||
true => [
|
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
throw new \LogicException('Expected an instance of Chill\MainBundle\Entity\User.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$activeTab = match ($filter) {
|
||||||
|
2 => 'referrer',
|
||||||
|
4 => 'referrer_to_works',
|
||||||
|
6 => 'both',
|
||||||
|
8 => 'intervening',
|
||||||
|
default => 'referrer',
|
||||||
|
};
|
||||||
|
|
||||||
|
$statusAndDateFilter = $this->buildStatusAndDateFilter($filter);
|
||||||
|
|
||||||
|
$status = $statusAndDateFilter->getCheckboxData('statusFilter');
|
||||||
|
$from = null;
|
||||||
|
$to = null;
|
||||||
|
|
||||||
|
if ('intervening' === $activeTab) {
|
||||||
|
$interventionBetweenDates = $statusAndDateFilter->getDateRangeData('interventionBetweenDates');
|
||||||
|
$from = $interventionBetweenDates['from'];
|
||||||
|
$to = $interventionBetweenDates['to'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$steps = [];
|
||||||
|
|
||||||
|
if (in_array('is_open', $status, true)) {
|
||||||
|
$steps[] = [
|
||||||
|
...$steps,
|
||||||
AccompanyingPeriod::STEP_CONFIRMED,
|
AccompanyingPeriod::STEP_CONFIRMED,
|
||||||
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG,
|
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_LONG,
|
||||||
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT,
|
AccompanyingPeriod::STEP_CONFIRMED_INACTIVE_SHORT,
|
||||||
],
|
];
|
||||||
false => [
|
}
|
||||||
AccompanyingPeriod::STEP_CLOSED,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
$total = $this->accompanyingPeriodRepository->countBy(['user' => $this->getUser(), 'step' => $steps]);
|
if (in_array('is_closed', $status, true)) {
|
||||||
$pagination = $this->paginatorFactory->create($total);
|
$steps[] = AccompanyingPeriod::STEP_CLOSED;
|
||||||
$accompanyingPeriods = $this->accompanyingPeriodRepository->findBy(
|
}
|
||||||
['user' => $this->getUser(), 'step' => $steps],
|
|
||||||
['openingDate' => 'DESC'],
|
$total = $this->accompanyingPeriodACLAwareRepository->countByUserAssociation($user, $steps, $from, $to, $filter);
|
||||||
$pagination->getItemsPerPage(),
|
$paginator = $this->paginatorFactory->create($total);
|
||||||
$pagination->getCurrentPageFirstItemNumber()
|
$accompanyingPeriods = $this->accompanyingPeriodACLAwareRepository->findByUserAssociation(
|
||||||
|
$user,
|
||||||
|
$steps,
|
||||||
|
$from,
|
||||||
|
$to,
|
||||||
|
$filter,
|
||||||
|
$paginator->getCurrentPageFirstItemNumber(),
|
||||||
|
$paginator->getItemsPerPage(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->render('@ChillPerson/AccompanyingPeriod/user_periods_list.html.twig', [
|
return $this->render('@ChillPerson/AccompanyingPeriod/user_periods_list.html.twig', [
|
||||||
'accompanyingPeriods' => $accompanyingPeriods,
|
'accompanyingPeriods' => $accompanyingPeriods,
|
||||||
'pagination' => $pagination,
|
'pagination' => $paginator,
|
||||||
'active' => $active,
|
'activeTab' => $activeTab,
|
||||||
|
'filter' => $filter,
|
||||||
|
'statusFilter' => $statusAndDateFilter,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,4 +122,29 @@ class UserAccompanyingPeriodController extends AbstractController
|
|||||||
'pagination' => $pagination,
|
'pagination' => $pagination,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildStatusAndDateFilter(int $filter)
|
||||||
|
{
|
||||||
|
$filterBuilder = $this->filterOrderHelperFactory
|
||||||
|
->create(self::class)
|
||||||
|
->addCheckbox(
|
||||||
|
'statusFilter',
|
||||||
|
['is_open', 'is_closed'],
|
||||||
|
['is_open'],
|
||||||
|
array_map(
|
||||||
|
static fn (string $s) => 'my_parcours_filters.'.$s,
|
||||||
|
['is_open', 'is_closed']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (8 === $filter) {
|
||||||
|
$filterBuilder->addDateRange(
|
||||||
|
'interventionBetweenDates',
|
||||||
|
$this->translator->trans('Since'),
|
||||||
|
new \DateTimeImmutable('-6 months'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filterBuilder->build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,4 +365,90 @@ final readonly class AccompanyingPeriodACLAwareRepository implements Accompanyin
|
|||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findByUserAssociation(User $user, array $steps, ?\DateTimeImmutable $from, ?\DateTimeImmutable $to, int $filter, ?int $start = 0, ?int $limit = 1000): array
|
||||||
|
{
|
||||||
|
$qb = $this->buildQueryByUserAssociation($user, $steps, $from, $to, $filter);
|
||||||
|
|
||||||
|
$qb->addOrderBy('acp.openingDate', 'DESC');
|
||||||
|
|
||||||
|
if (null !== $start) {
|
||||||
|
$qb->setFirstResult($start);
|
||||||
|
}
|
||||||
|
if (null !== $limit) {
|
||||||
|
$qb->setMaxResults($limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $qb->getQuery()->getResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countByUserAssociation(User $user, array $steps, ?\DateTimeImmutable $from, ?\DateTimeImmutable $to, int $filter): int
|
||||||
|
{
|
||||||
|
$qb = $this->buildQueryByUserAssociation($user, $steps, $from, $to, $filter);
|
||||||
|
|
||||||
|
$qb->select('COUNT(DISTINCT acp.id)');
|
||||||
|
|
||||||
|
return $qb->getQuery()->getSingleScalarResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildQueryByUserAssociation(User $user, array $steps, ?\DateTimeImmutable $from, ?\DateTimeImmutable $to, int $filter): QueryBuilder
|
||||||
|
{
|
||||||
|
$qb = $this->accompanyingPeriodRepository->createQueryBuilder('acp');
|
||||||
|
|
||||||
|
// Create an andX expression to hold the user association conditions
|
||||||
|
$whereUserAssociation = $qb->expr()->andX();
|
||||||
|
|
||||||
|
if (($filter & self::USER_IS_REFERRER) > 0) {
|
||||||
|
$whereUserAssociation->add($qb->expr()->eq('acp.user', ':user'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($filter & self::USER_IS_WORK_REFERRER) > 0) {
|
||||||
|
$whereUserAssociation->add(
|
||||||
|
$qb->expr()->exists(
|
||||||
|
'SELECT 1
|
||||||
|
FROM '.AccompanyingPeriod\AccompanyingPeriodWork::class.' subw
|
||||||
|
JOIN subw.referrersHistory subw_ref_history
|
||||||
|
WHERE subw.id = acpw.id
|
||||||
|
AND subw_ref_history.user = :user
|
||||||
|
AND subw_ref_history.endDate IS NULL'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$qb->innerJoin('acp.works', 'acpw');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($filter & self::USER_IS_INTERVENING) > 0) {
|
||||||
|
|
||||||
|
$expr = 'SELECT 1
|
||||||
|
FROM '.AccompanyingPeriod\AccompanyingPeriodInfo::class.' info
|
||||||
|
WHERE info.accompanyingPeriod = acp
|
||||||
|
AND info.user = :user';
|
||||||
|
|
||||||
|
if (null !== $from) {
|
||||||
|
$expr .= ' AND info.infoDate >= :from';
|
||||||
|
$qb->setParameter('from', $from);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $to) {
|
||||||
|
$expr .= ' AND info.infoDate <= :to';
|
||||||
|
$qb->setParameter('to', $to);
|
||||||
|
}
|
||||||
|
|
||||||
|
$whereUserAssociation->add(
|
||||||
|
$qb->expr()->exists($expr)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the compound condition to the query builder
|
||||||
|
$qb->andWhere($whereUserAssociation);
|
||||||
|
|
||||||
|
// Apply the steps condition
|
||||||
|
$qb->andWhere($qb->expr()->in('acp.step', ':steps'));
|
||||||
|
|
||||||
|
// Set the remaining parameters
|
||||||
|
$qb->setParameter('user', $user)
|
||||||
|
->setParameter('steps', $steps);
|
||||||
|
|
||||||
|
return $qb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,22 @@ use Chill\PersonBundle\Entity\Person;
|
|||||||
|
|
||||||
interface AccompanyingPeriodACLAwareRepositoryInterface
|
interface AccompanyingPeriodACLAwareRepositoryInterface
|
||||||
{
|
{
|
||||||
|
public const USER_IS_REFERRER = 0b0010; // 2 in decimal
|
||||||
|
public const USER_IS_WORK_REFERRER = 0b0100; // 4 in decimal
|
||||||
|
public const USER_IS_INTERVENING = 0b1000; // 8 in decimal
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds associations for a given user within a specific date range and step filters.
|
||||||
|
*
|
||||||
|
* @param \DateTimeImmutable|null $from the start date for filtering when intervention in accompanying period took place
|
||||||
|
* @param \DateTimeImmutable|null $to the end date for filtering when intervention in accompanying period took place
|
||||||
|
*
|
||||||
|
* @return array the list of user associations matching the given criteria
|
||||||
|
*/
|
||||||
|
public function findByUserAssociation(User $user, array $steps, ?\DateTimeImmutable $from, ?\DateTimeImmutable $to, int $filter, ?int $start = 0, ?int $limit = 1000): array;
|
||||||
|
|
||||||
|
public function countByUserAssociation(User $user, array $steps, ?\DateTimeImmutable $from, ?\DateTimeImmutable $to, int $filter): int;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array|UserJob[] $jobs
|
* @param array|UserJob[] $jobs
|
||||||
* @param array|Scope[] $services
|
* @param array|Scope[] $services
|
||||||
|
@ -62,6 +62,18 @@ final readonly class AccompanyingPeriodRepository implements ObjectRepository
|
|||||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findOneBy(array $criteria): ?AccompanyingPeriod
|
||||||
|
{
|
||||||
|
return $this->findOneBy($criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassName()
|
||||||
|
{
|
||||||
|
return AccompanyingPeriod::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CUSTOM FIND BY METHODS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array|AccompanyingPeriod[]
|
* @return array|AccompanyingPeriod[]
|
||||||
*/
|
*/
|
||||||
@ -87,16 +99,6 @@ final readonly class AccompanyingPeriodRepository implements ObjectRepository
|
|||||||
return $qb;
|
return $qb;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findOneBy(array $criteria): ?AccompanyingPeriod
|
|
||||||
{
|
|
||||||
return $this->findOneBy($criteria);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClassName()
|
|
||||||
{
|
|
||||||
return AccompanyingPeriod::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildQueryByRecentUserHistory(User $user, \DateTimeImmutable $since): QueryBuilder
|
private function buildQueryByRecentUserHistory(User $user, \DateTimeImmutable $since): QueryBuilder
|
||||||
{
|
{
|
||||||
$qb = $this->repository->createQueryBuilder('a');
|
$qb = $this->repository->createQueryBuilder('a');
|
||||||
|
@ -19,14 +19,38 @@
|
|||||||
|
|
||||||
<ul class="nav nav-pills justify-content-center">
|
<ul class="nav nav-pills justify-content-center">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active == true %}active{% endif %}" aria-current="page" href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': true}) }}">{{ ['Confirmed'|trans, 'course.inactive_short'|trans, 'course.inactive_long'|trans]|join(', ') }}</a>
|
<a class="nav-link {% if activeTab == 'referrer' %}active{% endif %}"
|
||||||
|
aria-current="page"
|
||||||
|
href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': true, 'filter': 2}) }}">
|
||||||
|
{{ 'my_parcours_filters.referrer_parcours'|trans }}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item ">
|
<li class="nav-item">
|
||||||
<a class="nav-link {% if active == false %}active{% endif %}" href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': false}) }}">{{ 'course.closed'|trans }}</a>
|
<a class="nav-link {% if activeTab == 'referrer_to_works' %}active{% endif %}"
|
||||||
|
aria-current="page"
|
||||||
|
href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': true, 'filter': 4}) }}">
|
||||||
|
{{ 'my_parcours_filters.referrer_acpw'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {% if activeTab == 'both' %}active{% endif %}"
|
||||||
|
aria-current="page"
|
||||||
|
href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': true, 'filter': 6}) }}">
|
||||||
|
{{ 'my_parcours_filters.referrer_parcours_and_acpw'|trans }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {% if activeTab == 'intervening' %}active{% endif %}"
|
||||||
|
aria-current="page"
|
||||||
|
href="{{ chill_path_forward_return_path('chill_person_accompanying_period_user', {'active': true, 'filter': 8}) }}">
|
||||||
|
{{ 'my_parcours_filters.parcours_intervening'|trans }}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>{{ 'Number of periods'|trans }}: <span class="badge rounded-pill bg-primary">{{ pagination.totalItems }}</span></p>
|
{{ statusFilter|chill_render_filter_order_helper }}
|
||||||
|
|
||||||
|
<p>{{ 'Number of periods'|trans }}: <span class="badge rounded-pill bg-primary mt-3">{{ pagination.totalItems }}</span></p>
|
||||||
|
|
||||||
<div class="flex-table accompanyingcourse-list">
|
<div class="flex-table accompanyingcourse-list">
|
||||||
{% for period in accompanyingPeriods %}
|
{% for period in accompanyingPeriods %}
|
||||||
|
@ -1497,3 +1497,11 @@ entity_display_title:
|
|||||||
Evaluation (n°%eval%): "Évaluation (n°%eval%)"
|
Evaluation (n°%eval%): "Évaluation (n°%eval%)"
|
||||||
Work (n°%w%): "Action d'accompagnement (n°%w%)"
|
Work (n°%w%): "Action d'accompagnement (n°%w%)"
|
||||||
Accompanying Course (n°%w%): "Parcours d'accompagnement (n°%w%)"
|
Accompanying Course (n°%w%): "Parcours d'accompagnement (n°%w%)"
|
||||||
|
|
||||||
|
my_parcours_filters:
|
||||||
|
referrer_parcours_and_acpw: Agent traitant ou réferent
|
||||||
|
referrer_acpw: Agent traitant d'une action
|
||||||
|
referrer_parcours: Réferent
|
||||||
|
parcours_intervening: Intervenant
|
||||||
|
is_open: Parcours ouverts
|
||||||
|
is_closed: Parcours clôturés
|
||||||
|
Loading…
x
Reference in New Issue
Block a user