mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-13 05:44:24 +00:00
Add ticket listing and related enhancements
Added a new functionality for listing tickets with the ability for the user to order the list. A method was added to the User class to identify if an object is an instance of User. Similarly, a method was added to the UserGroup class. User.php, UserGroup.php, TicketRepository.php, and TicketRepositoryInterface.php were updated. A new TicketListController, MotiveRepository, and SectionMenuBuilder were created. Translations were included, and services.yaml was updated.
This commit is contained in:
parent
50025044d3
commit
26dfa9b028
@ -608,4 +608,14 @@ class User implements UserInterface, \Stringable, PasswordAuthenticatedUserInter
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current object is an instance of User.
|
||||
*
|
||||
* @return bool returns true if the current object is an instance of User, false otherwise
|
||||
*/
|
||||
public function isUser(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -136,4 +136,16 @@ class UserGroup
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current object is an instance of the UserGroup class.
|
||||
*
|
||||
* In use in twig template, to discriminate when there an object can be polymorphic.
|
||||
*
|
||||
* @return bool returns true if the current object is an instance of UserGroup, false otherwise
|
||||
*/
|
||||
public function isUserGroup(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TicketBundle\Controller;
|
||||
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
|
||||
use Chill\MainBundle\Templating\Listing\FilterOrderHelperFactory;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
use Chill\TicketBundle\Entity\Motive;
|
||||
use Chill\TicketBundle\Repository\MotiveRepository;
|
||||
use Chill\TicketBundle\Repository\TicketRepositoryInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Twig\Environment;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
|
||||
final readonly class TicketListController
|
||||
{
|
||||
public function __construct(
|
||||
private Security $security,
|
||||
private TicketRepositoryInterface $ticketRepository,
|
||||
private Environment $twig,
|
||||
private FilterOrderHelperFactory $filterOrderHelperFactory,
|
||||
// private MotiveRepository $motiveRepository,
|
||||
// private TranslatableStringHelperInterface $translatableStringHelper,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @throws RuntimeError
|
||||
* @throws SyntaxError
|
||||
* @throws LoaderError
|
||||
*/
|
||||
#[Route('/{_locale}/ticket/ticket/list', name: 'chill_ticket_ticket_list')]
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
if (!$this->security->isGranted('ROLE_USER')) {
|
||||
throw new AccessDeniedHttpException('only user can access this page');
|
||||
}
|
||||
|
||||
$filter = $this->buildFilter();
|
||||
|
||||
$tickets = $this->ticketRepository->findAllOrdered();
|
||||
|
||||
return new Response(
|
||||
$this->twig->render('@ChillTicket/Ticket/list.html.twig', [
|
||||
'tickets' => $tickets,
|
||||
'filter' => $filter,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
private function buildFilter(): FilterOrderHelper
|
||||
{
|
||||
// $motives = $this->motiveRepository->findAll();
|
||||
|
||||
return $this->filterOrderHelperFactory
|
||||
->create(__CLASS__)
|
||||
->addSingleCheckbox('to_me', 'chill_ticket.list.filter.to_me')
|
||||
->addSingleCheckbox('in_alert', 'chill_ticket.list.filter.in_alert')
|
||||
->addDateRange('created_between', 'chill_ticket.list.filter.created_between')
|
||||
/*
|
||||
->addEntityChoice('by_motive', 'chill_ticket.list.filter.by_motive', Motive::class, $motives, [
|
||||
'choice_label' => fn (Motive $motive) => $this->translatableStringHelper->localize($motive->getLabel()),
|
||||
'expanded' => true,
|
||||
'multiple' => true,
|
||||
'attr' => ['class' => 'select2'],
|
||||
])
|
||||
*/
|
||||
->build();
|
||||
}
|
||||
}
|
29
src/Bundle/ChillTicketBundle/src/Menu/SectionMenuBuilder.php
Normal file
29
src/Bundle/ChillTicketBundle/src/Menu/SectionMenuBuilder.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TicketBundle\Menu;
|
||||
|
||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||
use Knp\Menu\MenuItem;
|
||||
|
||||
class SectionMenuBuilder implements LocalMenuBuilderInterface
|
||||
{
|
||||
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||
{
|
||||
$menu->addChild('Tickets', ['route' => 'chill_ticket_ticket_list'])
|
||||
->setExtras(['order' => 250]);
|
||||
}
|
||||
|
||||
public static function getMenuIds(): array
|
||||
{
|
||||
return ['section'];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\TicketBundle\Repository;
|
||||
|
||||
use Chill\TicketBundle\Entity\Motive;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @template-extends ServiceEntityRepository<Motive>
|
||||
*/
|
||||
class MotiveRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Motive::class);
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ final readonly class TicketRepository implements TicketRepositoryInterface
|
||||
{
|
||||
private ObjectRepository $repository;
|
||||
|
||||
public function __construct(EntityManagerInterface $objectManager)
|
||||
public function __construct(private EntityManagerInterface $objectManager)
|
||||
{
|
||||
$this->repository = $objectManager->getRepository($this->getClassName());
|
||||
}
|
||||
@ -34,6 +34,14 @@ final readonly class TicketRepository implements TicketRepositoryInterface
|
||||
return $this->repository->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<Ticket>
|
||||
*/
|
||||
public function findAllOrdered(): array
|
||||
{
|
||||
return $this->objectManager->createQuery('SELECT t FROM '.$this->getClassName().' t ORDER BY t.id DESC')->getResult();
|
||||
}
|
||||
|
||||
public function findBy(array $criteria, ?array $orderBy = null, ?int $limit = null, ?int $offset = null): array
|
||||
{
|
||||
return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
|
||||
|
@ -20,4 +20,9 @@ use Doctrine\Persistence\ObjectRepository;
|
||||
interface TicketRepositoryInterface extends ObjectRepository
|
||||
{
|
||||
public function findOneByExternalRef(string $extId): ?Ticket;
|
||||
|
||||
/**
|
||||
* @return list<Ticket>
|
||||
*/
|
||||
public function findAllOrdered(): array;
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
{% extends '@ChillMain/layout.html.twig' %}
|
||||
|
||||
{% block title 'chill_ticket.list.title'|trans %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
{{ filter|chill_render_filter_order_helper }}
|
||||
|
||||
{% if tickets|length == 0 %}
|
||||
<p class="chill-no-data-statement">{{ chill_ticket.list.no_tickets }}</p>
|
||||
{% else %}
|
||||
<div class="flex-table">
|
||||
{% for ticket in tickets %}
|
||||
<div class="item-bloc">
|
||||
<div class="item-row">
|
||||
<div class="wrap-header">
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">
|
||||
{% if ticket.motive is not null %}
|
||||
<span class="h2" style="color: var(--bs-chill-blue); font-variant: all-small-caps">
|
||||
{{ ticket.motive.label|localize_translatable_string }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="h3" style="color: var(--bs-chill-blue); font-style: italic">Sans motif</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="wh-col">
|
||||
<p style="font-size: 1.5rem;"><span class="badge text-bg-chill-green text-white">Ouvert</span></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wh-row">
|
||||
<div class="wh-col">
|
||||
#{{ ticket.id }}
|
||||
</div>
|
||||
<div class="wh-col">
|
||||
{% if ticket.createdAt is not null %}
|
||||
<span title="{{ ticket.createdAt|format_datetime('long', 'long') }}" style="font-style: italic;">{{ ticket.createdAt|ago|capitalize }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if ticket.persons|length > 0 or ticket.currentAddressee|length > 0 %}
|
||||
<div class="item-row separator">
|
||||
<div class="wrap-list">
|
||||
{% if ticket.persons|length > 0 %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3 class="mb-2">Patient concerné</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for p in ticket.persons %}
|
||||
{% include '@ChillMain/OnTheFly/_insert_vue_onthefly.html.twig' with {
|
||||
targetEntity: { name: 'person', id: p.id },
|
||||
action: 'show',
|
||||
displayBadge: true,
|
||||
buttonText: p|chill_entity_render_string,
|
||||
isDead: p.deathdate is not null
|
||||
} %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if ticket.currentAddressee|length > 0 %}
|
||||
<div class="wl-row">
|
||||
<div class="wl-col title"><h3 class="mb-2">Destinataires</h3></div>
|
||||
<div class="wl-col list">
|
||||
{% for d in ticket.currentAddressee %}
|
||||
{% if d.isUser is defined and d.isUser %}
|
||||
{{ d|chill_entity_render_box }}
|
||||
{% elseif d.isUserGroup is defined and d.isUserGroup %}
|
||||
{{ d|chill_entity_render_box }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="item-row separator">
|
||||
<ul class="record_actions">
|
||||
<li>
|
||||
<a class="btn btn-update" href="{{ chill_path_add_return_path('chill_ticket_ticket_edit', {'id': ticket.id}) }}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<ul class="record_actions sticky-form-buttons">
|
||||
<li>
|
||||
<a href="{{ chill_path_add_return_path('chill_ticket_createticket__invoke') }}" class="btn btn-create">{{ 'Create'|trans }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock %}
|
@ -17,5 +17,8 @@ services:
|
||||
Chill\TicketBundle\Serializer\:
|
||||
resource: '../Serializer/'
|
||||
|
||||
Chill\TicketBundle\Menu\:
|
||||
resource: '../Menu/'
|
||||
|
||||
Chill\TicketBundle\DataFixtures\:
|
||||
resource: '../DataFixtures/'
|
||||
|
@ -0,0 +1,9 @@
|
||||
chill_ticket:
|
||||
list:
|
||||
title: Tickets
|
||||
filter:
|
||||
to_me: Tickets qui me sont attribués
|
||||
in_alert: Tickets en alerte (délai de résolution dépassé)
|
||||
created_between: Créés entre
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user