mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 01:08:26 +00:00 
			
		
		
		
	#365 Add works and activities counter
This commit is contained in:
		
							
								
								
									
										9
									
								
								.changes/unreleased/Feature-20250319-090004.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.changes/unreleased/Feature-20250319-090004.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||
| @@ -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]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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, | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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. | ||||
|      */ | ||||
|   | ||||
| @@ -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'); | ||||
|   | ||||
| @@ -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; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -201,7 +201,7 @@ | ||||
|         {% endif %} | ||||
|  | ||||
|         {% if accompanyingCourse.step != 'DRAFT' %} | ||||
|             <div class="mbloc col col-sm-6 col-lg-4"> | ||||
|             <div class="mbloc col col-sm-6 col-lg-8 col-xxl-4"> | ||||
|                 <div class="notification-counter"> | ||||
|                     <h4 class="item-key">{{ 'notification.Notifications'|trans }}</h4> | ||||
|                     {% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) %} | ||||
| @@ -238,6 +238,31 @@ | ||||
|                 </div> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|  | ||||
|         {% if counters.activities > 0 %} | ||||
|             <div class="mbloc col col-sm-6 col-lg-4"> | ||||
|                 <div class="count-activities"> | ||||
|                     <div class="count-item">{{ counters.activities }}</div> | ||||
|                     <div class="count-item-label"> | ||||
|                         {% if counters.activities == 1 %} | ||||
|                             {{ 'Activity'|trans }} | ||||
|                         {% else %} | ||||
|                             {{ 'Activities'|trans }} | ||||
|                         {% endif %} | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|  | ||||
|         {% if counters.works > 0 %} | ||||
|             <div class="mbloc col col-sm-6 col-lg-4"> | ||||
|                 <div class="count-works"> | ||||
|                     <div class="count-item">{{ counters.openWorks }} / {{ counters.works }}</div> | ||||
|                     <div class="count-item-label">{{ 'accompanying_course_work.On-going works over total'|trans }}</div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         {% endif %} | ||||
|  | ||||
|     </div> | ||||
|  | ||||
|     <div class="social-actions my-4"> | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|  | ||||
| # | ||||
|   | ||||
		Reference in New Issue
	
	Block a user