mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
#365 Add works and activities counter
This commit is contained in:
parent
1c1f418b18
commit
40e373a9c7
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;
|
namespace Chill\ActivityBundle\Menu;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Entity\Activity;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
use Chill\PersonBundle\Entity\AccompanyingPeriod;
|
||||||
@ -23,22 +24,30 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||||||
*/
|
*/
|
||||||
class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
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)
|
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||||
{
|
{
|
||||||
$period = $parameters['accompanyingCourse'];
|
$period = $parameters['accompanyingCourse'];
|
||||||
|
|
||||||
|
$activities = $this->managerRegistry->getManager()->getRepository(Activity::class)->findBy(
|
||||||
|
['accompanyingPeriod' => $period]
|
||||||
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
AccompanyingPeriod::STEP_DRAFT !== $period->getStep()
|
AccompanyingPeriod::STEP_DRAFT !== $period->getStep()
|
||||||
&& $this->security->isGranted(ActivityVoter::SEE, $period)
|
&& $this->security->isGranted(ActivityVoter::SEE, $period)
|
||||||
) {
|
) {
|
||||||
$menu->addChild($this->translator->trans('Activity'), [
|
$menu->addChild($this->translator->trans('Activities'), [
|
||||||
'route' => 'chill_activity_activity_list',
|
'route' => 'chill_activity_activity_list',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'accompanying_period_id' => $period->getId(),
|
'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;
|
namespace Chill\ActivityBundle\Menu;
|
||||||
|
|
||||||
|
use Chill\ActivityBundle\Repository\ActivityACLAwareRepositoryInterface;
|
||||||
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
use Chill\ActivityBundle\Security\Authorization\ActivityVoter;
|
||||||
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
use Chill\MainBundle\Routing\LocalMenuBuilderInterface;
|
||||||
use Chill\PersonBundle\Entity\Person;
|
use Chill\PersonBundle\Entity\Person;
|
||||||
@ -23,13 +24,20 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||||||
*/
|
*/
|
||||||
final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface
|
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)
|
public function buildMenu($menuId, MenuItem $menu, array $parameters)
|
||||||
{
|
{
|
||||||
/** @var Person $person */
|
/** @var Person $person */
|
||||||
$person = $parameters['person'];
|
$person = $parameters['person'];
|
||||||
|
|
||||||
|
|
||||||
|
$count = $this->activityACLAwareRepository->countByPerson($person, ActivityVoter::SEE);
|
||||||
|
|
||||||
if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) {
|
if ($this->authorizationChecker->isGranted(ActivityVoter::SEE, $person)) {
|
||||||
$menu->addChild(
|
$menu->addChild(
|
||||||
$this->translator->trans('Activities'),
|
$this->translator->trans('Activities'),
|
||||||
@ -38,7 +46,7 @@ final readonly class PersonMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
'routeParameters' => ['person_id' => $person->getId()],
|
'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'],
|
['date' => 'DESC', 'id' => 'DESC'],
|
||||||
);
|
);
|
||||||
|
|
||||||
$activities = \array_slice($activities, 0, 3);
|
|
||||||
|
|
||||||
$works = $this->workRepository->findByAccompanyingPeriod(
|
$works = $this->workRepository->findByAccompanyingPeriod(
|
||||||
$accompanyingCourse,
|
$accompanyingCourse,
|
||||||
['startDate' => 'DESC', 'endDate' => 'DESC'],
|
['startDate' => 'DESC', 'endDate' => 'DESC'],
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$counters = [
|
||||||
|
'activities' => count($activities),
|
||||||
|
'openWorks' => count($accompanyingCourse->getOpenWorks()),
|
||||||
|
'works' => count($works),
|
||||||
|
];
|
||||||
|
|
||||||
return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [
|
return $this->render('@ChillPerson/AccompanyingCourse/index.html.twig', [
|
||||||
'accompanyingCourse' => $accompanyingCourse,
|
'accompanyingCourse' => $accompanyingCourse,
|
||||||
'withoutHousehold' => $withoutHousehold,
|
'withoutHousehold' => $withoutHousehold,
|
||||||
'participationsByHousehold' => $accompanyingCourse->actualParticipationsByHousehold(),
|
'participationsByHousehold' => $accompanyingCourse->actualParticipationsByHousehold(),
|
||||||
'works' => $works,
|
'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;
|
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.
|
* Open a new participation for a person.
|
||||||
*/
|
*/
|
||||||
|
@ -71,7 +71,7 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
->setExtras(['order' => 30]);
|
->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',
|
'route' => 'chill_person_accompanying_period_comment_list',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'accompanying_period_id' => $period->getId(),
|
'accompanying_period_id' => $period->getId(),
|
||||||
@ -80,12 +80,15 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->security->isGranted(AccompanyingPeriodWorkVoter::SEE, $period)) {
|
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',
|
'route' => 'chill_person_accompanying_period_work_list',
|
||||||
'routeParameters' => [
|
'routeParameters' => [
|
||||||
'id' => $period->getId(),
|
'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');
|
$workflow = $this->registry->get($period, 'accompanying_period_lifecycle');
|
||||||
|
@ -304,5 +304,14 @@ div#dashboards {
|
|||||||
margin: 0;
|
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 %}
|
{% endif %}
|
||||||
|
|
||||||
{% if accompanyingCourse.step != 'DRAFT' %}
|
{% 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">
|
<div class="notification-counter">
|
||||||
<h4 class="item-key">{{ 'notification.Notifications'|trans }}</h4>
|
<h4 class="item-key">{{ 'notification.Notifications'|trans }}</h4>
|
||||||
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) %}
|
{% set notif_counter = chill_count_notifications('Chill\\PersonBundle\\Entity\\AccompanyingPeriod', accompanyingCourse.id) %}
|
||||||
@ -238,6 +238,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% 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>
|
||||||
|
|
||||||
<div class="social-actions my-4">
|
<div class="social-actions my-4">
|
||||||
|
@ -804,7 +804,7 @@ person_admin:
|
|||||||
|
|
||||||
# specific to accompanying period
|
# specific to accompanying period
|
||||||
accompanying_period:
|
accompanying_period:
|
||||||
deleted: Parcours d'accompagnment supprimé
|
deleted: Parcours d'accompagnement supprimé
|
||||||
dates: Période
|
dates: Période
|
||||||
dates_from_%opening_date%: Ouvert depuis le %opening_date%
|
dates_from_%opening_date%: Ouvert depuis le %opening_date%
|
||||||
dates_from_%opening_date%_to_%closing_date%: Ouvert du %opening_date% au %closing_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
|
administrative_location: Localisation administrative
|
||||||
comment is pinned: Le commentaire est épinglé
|
comment is pinned: Le commentaire est épinglé
|
||||||
comment is unpinned: Le commentaire est désépinglé
|
comment is unpinned: Le commentaire est désépinglé
|
||||||
|
|
||||||
show: Montrer
|
show: Montrer
|
||||||
hide: Masquer
|
hide: Masquer
|
||||||
closed periods: parcours clôturés
|
closed periods: parcours clôturés
|
||||||
@ -851,6 +852,7 @@ Social work configuration: Gestion des actions d'accompagnement social
|
|||||||
|
|
||||||
# Accompanying Course comments
|
# Accompanying Course comments
|
||||||
Accompanying Course Comment: Commentaire
|
Accompanying Course Comment: Commentaire
|
||||||
|
Accompanying Course Comments: Commentaires
|
||||||
Accompanying Course Comment list: Commentaires du parcours
|
Accompanying Course Comment list: Commentaires du parcours
|
||||||
pinned: épinglé
|
pinned: épinglé
|
||||||
Pin comment: Épingler
|
Pin comment: Épingler
|
||||||
@ -919,6 +921,7 @@ accompanying_course_work:
|
|||||||
date_filter: Filtrer par date
|
date_filter: Filtrer par date
|
||||||
types_filter: Filtrer par type d'action
|
types_filter: Filtrer par type d'action
|
||||||
user_filter: Filtrer par intervenant
|
user_filter: Filtrer par intervenant
|
||||||
|
On-going works over total: Actions en cours / Actions du parcours
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
Loading…
x
Reference in New Issue
Block a user