diff --git a/src/Bundle/ChillTicketBundle/src/Controller/TicketListController.php b/src/Bundle/ChillTicketBundle/src/Controller/TicketListController.php index f15329689..7a25bbbc5 100644 --- a/src/Bundle/ChillTicketBundle/src/Controller/TicketListController.php +++ b/src/Bundle/ChillTicketBundle/src/Controller/TicketListController.php @@ -11,38 +11,22 @@ declare(strict_types=1); 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 Chill\PersonBundle\Security\Authorization\PersonVoter; +use Chill\PersonBundle\Entity\Person; 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 { @@ -50,35 +34,22 @@ final readonly class TicketListController 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, - ]) + $this->twig->render('@ChillTicket/Ticket/list.html.twig') ); } - private function buildFilter(): FilterOrderHelper + #[Route('/{_locale}/ticket/by-person/{id}/list', name: 'chill_person_ticket_list')] + public function listByPerson(Request $request, Person $person): Response { - // $motives = $this->motiveRepository->findAll(); + if (!$this->security->isGranted(PersonVoter::SEE, $person)) { + throw new AccessDeniedHttpException('you are not allowed to see this person'); + } - return $this->filterOrderHelperFactory - ->create(self::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'], + return new Response( + $this->twig->render('@ChillTicket/Person/list.html.twig', [ + 'person' => $person, ]) - */ - ->build(); + ); } } diff --git a/src/Bundle/ChillTicketBundle/src/DataFixtures/ORM/LoadMotives.php b/src/Bundle/ChillTicketBundle/src/DataFixtures/ORM/LoadMotives.php index 044949d1e..d5191898e 100644 --- a/src/Bundle/ChillTicketBundle/src/DataFixtures/ORM/LoadMotives.php +++ b/src/Bundle/ChillTicketBundle/src/DataFixtures/ORM/LoadMotives.php @@ -46,7 +46,7 @@ final class LoadMotives extends Fixture implements FixtureGroupInterface continue; } - $labels = explode(' > ', $data[0]); + $labels = explode(' > ', (string) $data[0]); $parent = null; while (count($labels) > 0) { diff --git a/src/Bundle/ChillTicketBundle/src/Menu/PersonMenuBuilder.php b/src/Bundle/ChillTicketBundle/src/Menu/PersonMenuBuilder.php new file mode 100644 index 000000000..ce780f036 --- /dev/null +++ b/src/Bundle/ChillTicketBundle/src/Menu/PersonMenuBuilder.php @@ -0,0 +1,69 @@ + + */ +class PersonMenuBuilder implements LocalMenuBuilderInterface +{ + public function __construct( + private readonly AuthorizationCheckerInterface $authorizationChecker, + private readonly TranslatorInterface $translator, + private readonly TicketRepositoryInterface $ticketRepository, + ) {} + + /** + * @param array{person: Person} $parameters + * + * @return void + */ + public function buildMenu($menuId, MenuItem $menu, array $parameters) + { + /** @var Person $person */ + $person = $parameters['person']; + + if ($this->authorizationChecker->isGranted(PersonVoter::SEE, $person)) { + $menu->addChild($this->translator->trans('chill_ticket.list.title_menu'), [ + 'route' => 'chill_person_ticket_list', + 'routeParameters' => [ + 'id' => $person->getId(), + ], + ]) + ->setExtras([ + 'order' => 150, + 'counter' => 0 < ($nbTickets = $this->ticketRepository->countOpenedByPerson($person)) + ? $nbTickets : null, + ]); + } + } + + public static function getMenuIds(): array + { + return ['person']; + } +} diff --git a/src/Bundle/ChillTicketBundle/src/Repository/TicketRepository.php b/src/Bundle/ChillTicketBundle/src/Repository/TicketRepository.php index d68598510..d519a77cb 100644 --- a/src/Bundle/ChillTicketBundle/src/Repository/TicketRepository.php +++ b/src/Bundle/ChillTicketBundle/src/Repository/TicketRepository.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\TicketBundle\Repository; +use Chill\PersonBundle\Entity\Person; use Chill\TicketBundle\Entity\Ticket; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ObjectRepository; @@ -61,4 +62,19 @@ final readonly class TicketRepository implements TicketRepositoryInterface { return $this->repository->findOneBy(['externalRef' => $extId]); } + + /** + * Count tickets associated with a person where endDate is null. + */ + public function countOpenedByPerson(Person $person): int + { + return (int) $this->objectManager->createQuery( + 'SELECT COUNT(DISTINCT t.id) FROM '.$this->getClassName().' t + JOIN t.personHistories ph + WHERE ph.person = :person + AND ph.endDate IS NULL' + ) + ->setParameter('person', $person) + ->getSingleScalarResult(); + } } diff --git a/src/Bundle/ChillTicketBundle/src/Repository/TicketRepositoryInterface.php b/src/Bundle/ChillTicketBundle/src/Repository/TicketRepositoryInterface.php index 9dc713e72..c1e4cb188 100644 --- a/src/Bundle/ChillTicketBundle/src/Repository/TicketRepositoryInterface.php +++ b/src/Bundle/ChillTicketBundle/src/Repository/TicketRepositoryInterface.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\TicketBundle\Repository; use Chill\TicketBundle\Entity\Ticket; +use Chill\PersonBundle\Entity\Person; use Doctrine\Persistence\ObjectRepository; /** @@ -25,4 +26,6 @@ interface TicketRepositoryInterface extends ObjectRepository * @return list */ public function findAllOrdered(): array; + + public function countOpenedByPerson(Person $person): int; } diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts b/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts index 1c914570e..601f299ff 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts @@ -177,8 +177,12 @@ export interface Ticket extends BaseTicket<"ticket_ticket:extended"> { } export interface TicketFilters { + byPerson: Person[]; + byCreator: User[]; + byAddressee: UserGroupOrUser[]; byCurrentState: TicketState[]; byCurrentStateEmergency: TicketEmergencyState[]; + byMotives: Motive[]; byCreatedAfter: string; byCreatedBefore: string; byResponseTimeExceeded: boolean; @@ -198,7 +202,7 @@ export interface TicketFilterParams { byCreatedBefore?: string; byResponseTimeExceeded?: string; byAddresseeToMe?: boolean; - byTicketId?: number; + byTicketId?: number | null; } export interface TicketInitForm { diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue index 02040b008..df291989a 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue @@ -1,16 +1,23 @@ + {{ encore_entry_script_tags('vue_ticket_list') }} +{% endblock %} + +{% block content %} +

{{ ticketTitle }}

+ +
+ + +{% endblock %} diff --git a/src/Bundle/ChillTicketBundle/src/Resources/views/Ticket/list.html.twig b/src/Bundle/ChillTicketBundle/src/Resources/views/Ticket/list.html.twig index 3616ab207..b1597535a 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/views/Ticket/list.html.twig +++ b/src/Bundle/ChillTicketBundle/src/Resources/views/Ticket/list.html.twig @@ -1,4 +1,6 @@ {% extends '@ChillMain/layout.html.twig' %} +{% set ticketTitle = 'chill_ticket.list.title'|trans %} +{% block title %}{{ ticketTitle }}{% endblock %} {% block css %} {{ parent() }} @@ -7,13 +9,11 @@ {% block js %} {{ parent() }} - {{ encore_entry_script_tags('vue_ticket_list') }} {% endblock %} {% block content %} +

{{ ticketTitle }}