diff --git a/.changes/unreleased/Feature-20250319-090004.yaml b/.changes/unreleased/Feature-20250319-090004.yaml new file mode 100644 index 000000000..3b4528b29 --- /dev/null +++ b/.changes/unreleased/Feature-20250319-090004.yaml @@ -0,0 +1,9 @@ +kind: Feature +body: Add counters of actions and activities, with 2 boxes to (1) show the number + of active actions on total actions and (2) show the number of activities in a accompanying + period, and pills in menus for showing the number of active actions and the number + of activities. +time: 2025-03-19T09:00:04.152359515+01:00 +custom: + Issue: "365" + SchemaChange: No schema change diff --git a/src/Bundle/ChillActivityBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillActivityBundle/Menu/AccompanyingCourseMenuBuilder.php index b4990c0e3..5ed606637 100644 --- a/src/Bundle/ChillActivityBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillActivityBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Menu; +use Chill\ActivityBundle\Entity\Activity; use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\PersonBundle\Entity\AccompanyingPeriod; @@ -23,22 +24,30 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface { - public function __construct(protected Security $security, protected TranslatorInterface $translator) {} + public function __construct( + protected Security $security, + protected TranslatorInterface $translator, + private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry, + ) {} public function buildMenu($menuId, MenuItem $menu, array $parameters) { $period = $parameters['accompanyingCourse']; + $activities = $this->managerRegistry->getManager()->getRepository(Activity::class)->findBy( + ['accompanyingPeriod' => $period] + ); + if ( AccompanyingPeriod::STEP_DRAFT !== $period->getStep() && $this->security->isGranted(ActivityVoter::SEE, $period) ) { - $menu->addChild($this->translator->trans('Activity'), [ + $menu->addChild($this->translator->trans('Activities'), [ 'route' => 'chill_activity_activity_list', 'routeParameters' => [ 'accompanying_period_id' => $period->getId(), ], ]) - ->setExtras(['order' => 40]); + ->setExtras(['order' => 40, 'counter' => count($activities) > 0 ? count($activities) : null]); } } diff --git a/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php b/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php index 797d64eb6..5a13c5d65 100644 --- a/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php +++ b/src/Bundle/ChillActivityBundle/Menu/PersonMenuBuilder.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\ActivityBundle\Menu; +use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface; use Chill\ActivityBundle\Security\Authorization\ActivityVoter; use Chill\MainBundle\Routing\LocalMenuBuilderInterface; use Chill\PersonBundle\Entity\Person; @@ -23,13 +24,20 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface { - public function __construct(private AuthorizationCheckerInterface $authorizationChecker, private TranslatorInterface $translator) {} + public function __construct( + private readonly ActivityACLAwareRepositoryInterface $activityACLAwareRepository, + private AuthorizationCheckerInterface $authorizationChecker, + private TranslatorInterface $translator, + ) {} public function buildMenu($menuId, MenuItem $menu, array $parameters) { /** @var Person $person */ $person = $parameters['person']; + + $count = $this->activityACLAwareRepository->countByPerson($person, ActivityVoter::SEE); + if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) { $menu->addChild( $this->translator->trans('Activities'), @@ -38,7 +46,7 @@ final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface 'routeParameters' => ['person_id' => $person->getId()], ] ) - ->setExtra('order', 201); + ->setExtras(['order' => 201, 'counter' => $count > 0 ? $count : null]); } } diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php index 754be808a..0cb3c1473 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseController.php @@ -204,20 +204,25 @@ final class AccompanyingCourseController extends \Symfony\Bundle\FrameworkBundle ['date' => 'DESC', 'id' => 'DESC'], ); - $activities = \array_slice($activities, 0, 3); - $works = $this->workRepository->findByAccompanyingPeriod( $accompanyingCourse, ['startDate' => 'DESC', 'endDate' => 'DESC'], 3 ); + $counters = [ + 'activities' => count($activities), + 'openWorks' => count($accompanyingCourse->getOpenWorks()), + 'works' => count($works), + ]; + return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [ 'accompanyingCourse' => $accompanyingCourse, 'withoutHousehold' => $withoutHousehold, 'participationsByHousehold' => $accompanyingCourse->actualParticipationsByHousehold(), 'works' => $works, - 'activities' => $activities, + 'activities' => \array_slice($activities, 0, 3), + 'counters' => $counters, ]); } diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 91c8714bb..d96814e1a 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -511,6 +511,14 @@ class AccompanyingPeriod implements return $this->getParticipationsContainsPerson($person)->count() > 0; } + public function getOpenWorks(): Collection + { + return $this->getWorks()->filter( + static fn (AccompanyingPeriodWork $work): bool => null === $work->getEndDate() + or $work->getEndDate() > new \DateTimeImmutable('today') + ); + } + /** * Open a new participation for a person. */ diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index 79cd19db9..8c93f461d 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -71,7 +71,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface ->setExtras(['order' => 30]); */ - $menu->addChild($this->translator->trans('Accompanying Course Comment'), [ + $menu->addChild($this->translator->trans('Accompanying Course Comments'), [ 'route' => 'chill_person_accompanying_period_comment_list', 'routeParameters' => [ 'accompanying_period_id' => $period->getId(), @@ -80,12 +80,15 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface } if ($this->security->isGranted(AccompanyingPeriodWorkVoter::SEE, $period)) { - $menu->addChild($this->translator->trans('Accompanying Course Action'), [ + $menu->addChild($this->translator->trans('Accompanying Course Actions'), [ 'route' => 'chill_person_accompanying_period_work_list', 'routeParameters' => [ 'id' => $period->getId(), ], ]) - ->setExtras(['order' => 40]); + ->setExtras([ + 'order' => 40, + 'counter' => count($period->getOpenWorks()) > 0 ? count($period->getOpenWorks()) : null, + ]); } $workflow = $this->registry->get($period, 'accompanying_period_lifecycle'); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss b/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss index 24f947100..afa163cf2 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss +++ b/src/Bundle/ChillPersonBundle/Resources/public/chill/chillperson.scss @@ -304,5 +304,14 @@ div#dashboards { margin: 0; } } + div.count-item { + font-size: 3rem; + text-align: center; + } + div.count-item-label { + font-size: 90%; + font-variant: all-small-caps; + text-align: center; + } } } diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig index 4ceadf7a1..3637de6c8 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/index.html.twig @@ -201,7 +201,7 @@ {% endif %} {% if accompanyingCourse.step != 'DRAFT' %} -
+

{{ 'notification.Notifications'|trans }}

{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) %} @@ -238,6 +238,31 @@
{% endif %} + + {% if counters.activities > 0 %} +
+
+
{{ counters.activities }}
+
+ {% if counters.activities == 1 %} + {{ 'Activity'|trans }} + {% else %} + {{ 'Activities'|trans }} + {% endif %} +
+
+
+ {% endif %} + + {% if counters.works > 0 %} +
+
+
{{ counters.openWorks }} / {{ counters.works }}
+
{{ 'accompanying_course_work.On-going works over total'|trans }}
+
+
+ {% endif %} +
diff --git a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml index 204d5a982..c2aee2cf0 100644 --- a/src/Bundle/ChillPersonBundle/translations/messages.fr.yml +++ b/src/Bundle/ChillPersonBundle/translations/messages.fr.yml @@ -804,7 +804,7 @@ person_admin: # specific to accompanying period accompanying_period: - deleted: Parcours d'accompagnment supprimé + deleted: Parcours d'accompagnement supprimé dates: Période dates_from_%opening_date%: Ouvert depuis le %opening_date% dates_from_%opening_date%_to_%closing_date%: Ouvert du %opening_date% au %closing_date% @@ -843,6 +843,7 @@ accompanying_course: administrative_location: Localisation administrative comment is pinned: Le commentaire est épinglé comment is unpinned: Le commentaire est désépinglé + show: Montrer hide: Masquer closed periods: parcours clôturés @@ -851,6 +852,7 @@ Social work configuration: Gestion des actions d'accompagnement social # Accompanying Course comments Accompanying Course Comment: Commentaire +Accompanying Course Comments: Commentaires Accompanying Course Comment list: Commentaires du parcours pinned: épinglé Pin comment: Épingler @@ -919,6 +921,7 @@ accompanying_course_work: date_filter: Filtrer par date types_filter: Filtrer par type d'action user_filter: Filtrer par intervenant + On-going works over total: Actions en cours / Actions du parcours #