diff --git a/.changes/unreleased/Fixed-20251112-170025.yaml b/.changes/unreleased/Fixed-20251112-170025.yaml new file mode 100644 index 000000000..6808564b5 --- /dev/null +++ b/.changes/unreleased/Fixed-20251112-170025.yaml @@ -0,0 +1,6 @@ +kind: Fixed +body: Display calendar items for which an invite was accepted on the mes rendez-vous page +time: 2025-11-12T17:00:25.58946528+01:00 +custom: + Issue: "463" + SchemaChange: No schema change diff --git a/src/Bundle/ChillCalendarBundle/Controller/CalendarAPIController.php b/src/Bundle/ChillCalendarBundle/Controller/CalendarAPIController.php index eb5812b4d..d3919d3a3 100644 --- a/src/Bundle/ChillCalendarBundle/Controller/CalendarAPIController.php +++ b/src/Bundle/ChillCalendarBundle/Controller/CalendarAPIController.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Controller; use Chill\CalendarBundle\Repository\CalendarRepository; +use Chill\CalendarBundle\Repository\InviteRepository; use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Serializer\Model\Collection; @@ -23,7 +24,10 @@ use Symfony\Component\Routing\Annotation\Route; class CalendarAPIController extends ApiController { - public function __construct(private readonly CalendarRepository $calendarRepository) {} + public function __construct( + private readonly CalendarRepository $calendarRepository, + private readonly InviteRepository $inviteRepository, + ) {} #[Route(path: '/api/1.0/calendar/calendar/by-user/{id}.{_format}', name: 'chill_api_single_calendar_list_by-user', requirements: ['_format' => 'json'])] public function listByUser(User $user, Request $request, string $_format): JsonResponse @@ -52,16 +56,37 @@ class CalendarAPIController extends ApiController throw new BadRequestHttpException('dateTo not parsable'); } - $total = $this->calendarRepository->countByUser($user, $dateFrom, $dateTo); - $paginator = $this->getPaginatorFactory()->create($total); - $ranges = $this->calendarRepository->findByUser( + // Get calendar items where user is the main user + $ownCalendars = $this->calendarRepository->findByUser( $user, $dateFrom, - $dateTo, - $paginator->getItemsPerPage(), - $paginator->getCurrentPageFirstItemNumber() + $dateTo ); + // Get calendar items from accepted invites + $acceptedInvites = $this->inviteRepository->findAcceptedInvitesByUserAndDateRange($user, $dateFrom, $dateTo); + $inviteCalendars = array_map(fn ($invite) => $invite->getCalendar(), $acceptedInvites); + + // Merge + $allCalendars = array_merge($ownCalendars, $inviteCalendars); + $uniqueCalendars = []; + $seenIds = []; + + foreach ($allCalendars as $calendar) { + $id = $calendar->getId(); + if (!in_array($id, $seenIds, true)) { + $seenIds[] = $id; + $uniqueCalendars[] = $calendar; + } + } + + $total = count($uniqueCalendars); + $paginator = $this->getPaginatorFactory()->create($total); + + $offset = $paginator->getCurrentPageFirstItemNumber(); + $limit = $paginator->getItemsPerPage(); + $ranges = array_slice($uniqueCalendars, $offset, $limit); + $collection = new Collection($ranges, $paginator); return $this->json($collection, Response::HTTP_OK, [], ['groups' => ['calendar:light']]); diff --git a/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php b/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php index 6cb34738c..8fe28f500 100644 --- a/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php +++ b/src/Bundle/ChillCalendarBundle/Repository/InviteRepository.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace Chill\CalendarBundle\Repository; use Chill\CalendarBundle\Entity\Invite; +use Chill\MainBundle\Entity\User; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; use Doctrine\Persistence\ObjectRepository; @@ -51,6 +52,52 @@ class InviteRepository implements ObjectRepository return $this->entityRepository->findOneBy($criteria); } + /** + * Find accepted invites for a user within a date range. + * + * @return array|Invite[] + */ + public function findAcceptedInvitesByUserAndDateRange(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): array + { + return $this->buildAcceptedInviteByUserAndDateRangeQuery($user, $from, $to) + ->getQuery() + ->getResult(); + } + + /** + * Count accepted invites for a user within a date range. + */ + public function countAcceptedInvitesByUserAndDateRange(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to): int + { + return $this->buildAcceptedInviteByUserAndDateRangeQuery($user, $from, $to) + ->select('COUNT(c)') + ->getQuery() + ->getSingleScalarResult(); + } + + public function buildAcceptedInviteByUserAndDateRangeQuery(User $user, \DateTimeImmutable $from, \DateTimeImmutable $to) + { + $qb = $this->entityRepository->createQueryBuilder('i'); + + return $qb + ->join('i.calendar', 'c') + ->where( + $qb->expr()->andX( + $qb->expr()->eq('i.user', ':user'), + $qb->expr()->eq('i.status', ':status'), + $qb->expr()->gte('c.startDate', ':startDate'), + $qb->expr()->lte('c.endDate', ':endDate'), + $qb->expr()->isNull('c.cancelReason') + ) + ) + ->setParameters([ + 'user' => $user, + 'status' => Invite::ACCEPTED, + 'startDate' => $from, + 'endDate' => $to, + ]); + } + public function getClassName(): string { return Invite::class;