diff --git a/src/Bundle/ChillActivityBundle/Form/ActivityType.php b/src/Bundle/ChillActivityBundle/Form/ActivityType.php index 8af7a204c..22bd45658 100644 --- a/src/Bundle/ChillActivityBundle/Form/ActivityType.php +++ b/src/Bundle/ChillActivityBundle/Form/ActivityType.php @@ -35,6 +35,7 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\CallbackTransformer; use Chill\PersonBundle\Form\DataTransformer\PersonToIdTransformer; use Chill\PersonBundle\Templating\Entity\SocialIssueRender; +use Chill\PersonBundle\Templating\Entity\SocialActionRender; class ActivityType extends AbstractType { @@ -46,6 +47,10 @@ class ActivityType extends AbstractType protected TranslatableStringHelper $translatableStringHelper; + protected SocialIssueRender $socialIssueRender; + + protected SocialActionRender $socialActionRender; + protected array $timeChoices; public function __construct ( @@ -54,7 +59,8 @@ class ActivityType extends AbstractType ObjectManager $om, TranslatableStringHelper $translatableStringHelper, array $timeChoices, - SocialIssueRender $socialIssueRender + SocialIssueRender $socialIssueRender, + SocialActionRender $socialActionRender ) { if (!$tokenStorage->getToken()->getUser() instanceof User) { throw new \RuntimeException("you should have a valid user"); @@ -66,6 +72,7 @@ class ActivityType extends AbstractType $this->translatableStringHelper = $translatableStringHelper; $this->timeChoices = $timeChoices; $this->socialIssueRender = $socialIssueRender; + $this->socialActionRender = $socialActionRender; } public function buildForm(FormBuilderInterface $builder, array $options): void @@ -124,7 +131,7 @@ class ActivityType extends AbstractType 'required' => $activityType->isRequired('socialActions'), 'class' => SocialAction::class, 'choice_label' => function (SocialAction $socialAction) { - return $this->translatableStringHelper->localize($socialAction->getTitle()); + return $this->socialActionRender->renderString($socialAction, []); }, 'multiple' => true, 'choices' => $accompanyingPeriod->getRecursiveSocialActions(), diff --git a/src/Bundle/ChillActivityBundle/config/services/form.yaml b/src/Bundle/ChillActivityBundle/config/services/form.yaml index 3da20cce5..fd034b2da 100644 --- a/src/Bundle/ChillActivityBundle/config/services/form.yaml +++ b/src/Bundle/ChillActivityBundle/config/services/form.yaml @@ -32,6 +32,7 @@ services: - "@chill.main.helper.translatable_string" - "%chill_activity.form.time_duration%" - '@Chill\PersonBundle\Templating\Entity\SocialIssueRender' + - '@Chill\PersonBundle\Templating\Entity\SocialActionRender' tags: - { name: form.type, alias: chill_activitybundle_activity } diff --git a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php index 71476fb78..a1d28483d 100644 --- a/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php +++ b/src/Bundle/ChillMainBundle/CRUD/Controller/AbstractCRUDController.php @@ -8,6 +8,11 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Validator\Validator\ValidatorInterface; use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\MainBundle\Pagination\PaginatorInterface; +use Chill\MainBundle\Security\Authorization\AuthorizationHelper; +use Chill\MainBundle\CRUD\Resolver\Resolver; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Translation\TranslatorInterface; class AbstractCRUDController extends AbstractController { @@ -248,4 +253,24 @@ class AbstractCRUDController extends AbstractController { return $this->get('validator'); } + + /** + * @return array + */ + public static function getSubscribedServices(): array + { + return \array_merge( + parent::getSubscribedServices(), + [ + 'chill_main.paginator_factory' => PaginatorFactory::class, + + 'translator' => TranslatorInterface::class, + AuthorizationHelper::class => AuthorizationHelper::class, + EventDispatcherInterface::class => EventDispatcherInterface::class, + Resolver::class => Resolver::class, + SerializerInterface::class => SerializerInterface::class, + 'validator' => ValidatorInterface::class, + ] + ); + } } diff --git a/src/Bundle/ChillMainBundle/Resources/public/js/date.js b/src/Bundle/ChillMainBundle/Resources/public/js/date.js index e49a05972..ec66da770 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/js/date.js +++ b/src/Bundle/ChillMainBundle/Resources/public/js/date.js @@ -48,8 +48,8 @@ const ISOToDatetime = (str) => { let [cal, times] = str.split('T'), [year, month, date] = cal.split('-'), - [time, timezone] = cal.split(times.charAt(9)), - [hours, minutes, seconds] = cal.split(':') + [time, timezone] = times.split(times.charAt(8)), + [hours, minutes, seconds] = time.split(':') ; return new Date(year, month-1, date, hours, minutes, seconds); diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkApiController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkApiController.php new file mode 100644 index 000000000..6263736b0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkApiController.php @@ -0,0 +1,23 @@ +getMethod()) { + case Request::METHOD_PUT: + return [ 'groups' => [ 'accompanying_period_work:edit' ] ]; + } + } + + return parent::getContextForSerialization($action, $request, $_format, $entity); + } + +} diff --git a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php index 1a5ab3925..077fa15d7 100644 --- a/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php +++ b/src/Bundle/ChillPersonBundle/Controller/AccompanyingCourseWorkController.php @@ -2,32 +2,44 @@ namespace Chill\PersonBundle\Controller; +use Chill\MainBundle\Pagination\PaginatorFactory; use Chill\PersonBundle\Entity\AccompanyingPeriod; +use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Serializer\SerializerInterface; use Symfony\Component\Translation\TranslatorInterface; +use Chill\PersonBundle\Repository\AccompanyingPeriod\AccompanyingPeriodWorkRepository; class AccompanyingCourseWorkController extends AbstractController { private TranslatorInterface $trans; private SerializerInterface $serializer; + private AccompanyingPeriodWorkRepository $workRepository; + private PaginatorFactory $paginator; - public function __construct(TranslatorInterface $trans, SerializerInterface $serializer) - { + public function __construct( + TranslatorInterface $trans, + SerializerInterface $serializer, + AccompanyingPeriodWorkRepository $workRepository, + PaginatorFactory $paginator + ) { $this->trans = $trans; $this->serializer = $serializer; + $this->workRepository = $workRepository; + $this->paginator = $paginator; } /** * @Route( * "{_locale}/person/accompanying-period/{id}/work/new", - * methods={"GET", "POST"} + * name="chill_person_accompanying_period_work_new", + * methods={"GET"} * ) */ - public function createWork(AccompanyingPeriod $period) + public function createWork(AccompanyingPeriod $period): Response { // TODO ACL @@ -49,4 +61,49 @@ class AccompanyingCourseWorkController extends AbstractController 'json' => $json ]); } + + /** + * @Route( + * "{_locale}/person/accompanying-period/work/{id}/edit", + * name="chill_person_accompanying_period_work_edit", + * methods={"GET"} + * ) + */ + public function editWork(AccompanyingPeriodWork $work): Response + { + // TODO ACL + $json = $this->serializer->normalize($work, 'json', [ "groups" => [ "read" ] ]); + return $this->render('@ChillPerson/AccompanyingCourseWork/edit.html.twig', [ + 'accompanyingCourse' => $work->getAccompanyingPeriod(), + 'work' => $work, + 'json' => $json + ]); + } + + /** + * @Route( + * "{_locale}/person/accompanying-period/{id}/work", + * name="chill_person_accompanying_period_work_list", + * methods={"GET"} + * ) + */ + public function listWorkByAccompanyingPeriod(AccompanyingPeriod $period): Response + { + // TODO ACL + $totalItems = $this->workRepository->countByAccompanyingPeriod($period); + $paginator = $this->paginator->create($totalItems); + + $works = $this->workRepository->findByAccompanyingPeriod( + $period, + ['startDate' => 'DESC', 'endDate' => 'DESC'], + $paginator->getItemsPerPage(), + $paginator->getCurrentPageFirstItemNumber() + ); + + return $this->render('@ChillPerson/AccompanyingCourseWork/list_by_accompanying_period.html.twig', [ + 'accompanyingCourse' => $period, + 'works' => $works, + 'paginator' => $paginator + ]); + } } diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php new file mode 100644 index 000000000..40d7068ce --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/SocialIssueApiController.php @@ -0,0 +1,21 @@ +where( + $query->expr()->orX( + $query->expr()->gt('e.desactivationDate', ':now'), + $query->expr()->isNull('e.desactivationDate') + ) + ); + $query->setParameter('now', new \DateTimeImmutable()); + } +} diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialWorkGoalApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialWorkGoalApiController.php new file mode 100644 index 000000000..2e678a0d0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/SocialWorkGoalApiController.php @@ -0,0 +1,40 @@ +goalRepository = $goalRepository; + $this->paginator = $paginator; + } + + public function listBySocialAction(Request $request, SocialAction $action): Response + { + $totalItems = $this->goalRepository->countBySocialActionWithDescendants($action); + $paginator = $this->getPaginatorFactory()->create($totalItems); + + $entities = $this->goalRepository->findBySocialActionWithDescendants($action, ["id" => "ASC"], + $paginator->getItemsPerPage(), $paginator->getCurrentPageFirstItemNumber()); + + $model = new Collection($entities, $paginator); + + return $this->json($model, Response::HTTP_OK, [], [ "groups" => [ "read" ]]); + } + +} diff --git a/src/Bundle/ChillPersonBundle/Controller/SocialWorkResultApiController.php b/src/Bundle/ChillPersonBundle/Controller/SocialWorkResultApiController.php new file mode 100644 index 000000000..24902a163 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Controller/SocialWorkResultApiController.php @@ -0,0 +1,47 @@ +resultRepository = $resultRepository; + } + + public function listBySocialAction(Request $request, SocialAction $action): Response + { + $totalItems = $this->resultRepository->countBySocialActionWithDescendants($action); + $paginator = $this->getPaginatorFactory()->create($totalItems); + + $entities = $this->resultRepository->findBySocialActionWithDescendants($action, ["id" => "ASC"], + $paginator->getItemsPerPage(), $paginator->getCurrentPageFirstItemNumber()); + + $model = new Collection($entities, $paginator); + + return $this->json($model, Response::HTTP_OK, [], [ "groups" => [ "read" ]]); + } + + public function listByGoal(Request $request, Goal $goal): Response + { + $totalItems = $this->resultRepository->countByGoal($goal); + $paginator = $this->getPaginatorFactory()->create($totalItems); + + $entities = $this->resultRepository->findByGoal($goal, ["id" => "ASC"], + $paginator->getItemsPerPage(), $paginator->getCurrentPageFirstItemNumber()); + + $model = new Collection($entities, $paginator); + + return $this->json($model, Response::HTTP_OK, [], [ "groups" => [ "read" ]]); + } +} diff --git a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php index 79dae3255..733579acb 100644 --- a/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php +++ b/src/Bundle/ChillPersonBundle/DependencyInjection/ChillPersonExtension.php @@ -488,6 +488,7 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac [ 'class' => \Chill\PersonBundle\Entity\SocialWork\SocialIssue::class, 'name' => 'social_work_social_issue', + 'controller' => \Chill\PersonBundle\Controller\SocialIssueApiController::class, 'base_path' => '/api/1.0/person/social-work/social-issue', 'base_role' => 'ROLE_USER', 'actions' => [ @@ -553,7 +554,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac 'methods' => [ Request::METHOD_GET => true, Request::METHOD_HEAD => true, - Request::METHOD_POST=> true, ] ], 'address' => [ @@ -567,25 +567,6 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ], ] ], - [ - 'class' => \Chill\PersonBundle\Entity\Household\Household::class, - 'name' => 'household', - 'base_path' => '/api/1.0/person/household', - // TODO: acl - 'base_role' => 'ROLE_USER', - 'actions' => [ - '_entity' => [ - 'methods' => [ - Request::METHOD_GET => true, - Request::METHOD_HEAD => true, - ], - 'roles' => [ - Request::METHOD_GET => 'ROLE_USER', - Request::METHOD_HEAD => 'ROLE_USER', - ] - ], - ] - ], [ 'class' => \Chill\PersonBundle\Entity\SocialWork\SocialAction::class, 'name' => 'social_action', @@ -629,6 +610,127 @@ class ChillPersonExtension extends Extension implements PrependExtensionInterfac ] ] ], + [ + 'class' => \Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork::class, + 'name' => 'accompanying_period_work', + 'base_path' => '/api/1.0/person/accompanying-course/work', + 'controller' => \Chill\PersonBundle\Controller\AccompanyingCourseWorkApiController::class, + // TODO: acl + 'base_role' => 'ROLE_USER', + 'actions' => [ + '_entity' => [ + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + Request::METHOD_PATCH => true, + Request::METHOD_PUT => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + Request::METHOD_PATCH => 'ROLE_USER', + Request::METHOD_PUT => 'ROLE_USER', + ] + ], + ] + ], + [ + 'class' => \Chill\PersonBundle\Entity\SocialWork\Result::class, + 'controller' => \Chill\PersonBundle\Controller\SocialWorkResultApiController::class, + 'name' => 'social_work_result', + 'base_path' => '/api/1.0/person/social-work/result', + 'base_role' => 'ROLE_USER', + 'actions' => [ + '_entity' => [ + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + '_index' => [ + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + 'by-social-action' => [ + 'single-collection' => 'collection', + 'path' => '/by-social-action/{id}.{_format}', + 'controller_action' => 'listBySocialAction', + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + 'by-goal' => [ + 'single-collection' => 'collection', + 'path' => '/by-goal/{id}.{_format}', + 'controller_action' => 'listByGoal', + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + ] + ], + [ + 'class' => \Chill\PersonBundle\Entity\SocialWork\Goal::class, + 'controller' => \Chill\PersonBundle\Controller\SocialWorkGoalApiController::class, + 'name' => 'social_work_goal', + 'base_path' => '/api/1.0/person/social-work/goal', + 'base_role' => 'ROLE_USER', + 'actions' => [ + '_entity' => [ + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + '_index' => [ + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + 'by-social-action' => [ + 'single-collection' => 'collection', + 'path' => '/by-social-action/{id}.{_format}', + 'controller_action' => 'listBySocialAction', + 'methods' => [ + Request::METHOD_GET => true, + Request::METHOD_HEAD => true, + ], + 'roles' => [ + Request::METHOD_GET => 'ROLE_USER', + Request::METHOD_HEAD => 'ROLE_USER', + ] + ], + ] + ], ] ]); } diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php index 11570945e..e80f37364 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWork.php @@ -4,6 +4,7 @@ namespace Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\PersonBundle\Entity\SocialWork\Result; use Chill\PersonBundle\Entity\SocialWork\SocialAction; +use Chill\PersonBundle\Entity\Person; use Chill\MainBundle\Entity\User; use Chill\PersonBundle\Entity\AccompanyingPeriod; use Chill\ThirdPartyBundle\Entity\ThirdParty; @@ -38,7 +39,7 @@ use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\Column(type="text") - * @Serializer\Groups({"read"}) + * @Serializer\Groups({"read", "accompanying_period_work:edit"}) */ private string $note = ""; @@ -50,7 +51,8 @@ use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\ManyToOne(targetEntity=SocialAction::class) - * @Serializer\Groups({"accompanying_period_work:create", "read"}) + * @Serializer\Groups({"read"}) + * @Serializer\Groups({"accompanying_period_work:create"}) */ private ?SocialAction $socialAction = null; @@ -83,13 +85,16 @@ use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\Column(type="date_immutable") * @Serializer\Groups({"accompanying_period_work:create"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) * @Serializer\Groups({"read"}) */ private \DateTimeImmutable $startDate; /** * @ORM\Column(type="date_immutable", nullable=true, options={"default":null}) - * @Serializer\Groups({"accompanying_period_work:create", "read"}) + * @Serializer\Groups({"accompanying_period_work:create"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"read"}) * @Assert\GreaterThan(propertyPath="startDate", * message="accompanying_course_work.The endDate should be greater than the start date" * ) @@ -99,6 +104,7 @@ use Symfony\Component\Validator\Constraints as Assert; /** * @ORM\ManyToOne(targetEntity=ThirdParty::class) * @Serializer\Groups({"read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) * * In schema : traitant */ @@ -115,13 +121,22 @@ use Symfony\Component\Validator\Constraints as Assert; private string $createdAutomaticallyReason = ""; /** - * @ORM\OneToMany(targetEntity=AccompanyingPeriodWorkGoal::class, mappedBy="accompanyingPeriodWork") + * @ORM\OneToMany( + * targetEntity=AccompanyingPeriodWorkGoal::class, + * mappedBy="accompanyingPeriodWork", + * cascade={"persist"}, + * orphanRemoval=true + * ) + * @Serializer\Groups({"read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) */ private Collection $goals; /** * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorks") * @ORM\JoinTable(name="chill_person_accompanying_period_work_result") + * @Serializer\Groups({"read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) */ private Collection $results; @@ -130,14 +145,27 @@ use Symfony\Component\Validator\Constraints as Assert; * @ORM\JoinTable(name="chill_person_accompanying_period_work_third_party") * * In schema : intervenants + * @Serializer\Groups({"read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) */ private Collection $thirdParties; + /** + * + * @ORM\ManyToMany(targetEntity=Person::class) + * @ORM\JoinTable(name="chill_person_accompanying_period_work_person") + * @Serializer\Groups({"read"}) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"accompanying_period_work:create"}) + */ + private Collection $persons; + public function __construct() { $this->goals = new ArrayCollection(); $this->results = new ArrayCollection(); $this->thirdParties = new ArrayCollection(); + $this->persons = new ArrayCollection(); } public function getId(): ?int @@ -255,7 +283,7 @@ use Symfony\Component\Validator\Constraints as Assert; return $this->endDate; } - public function setEndDate(\DateTimeInterface $endDate): self + public function setEndDate(?\DateTimeInterface $endDate = null): self { $this->endDate = $endDate; @@ -310,7 +338,7 @@ use Symfony\Component\Validator\Constraints as Assert; { if (!$this->goals->contains($goal)) { $this->goals[] = $goal; - $goal->setAccompanyingPeriodWork2($this); + $goal->setAccompanyingPeriodWork($this); } return $this; @@ -318,10 +346,10 @@ use Symfony\Component\Validator\Constraints as Assert; public function removeGoal(AccompanyingPeriodWorkGoal $goal): self { - if ($this->goals->removeElement($goal)) { + if ($this->goals->removeElement($goal)) { // set the owning side to null (unless already changed) - if ($goal->getAccompanyingPeriodWork2() === $this) { - $goal->setAccompanyingPeriodWork2(null); + if ($goal->getAccompanyingPeriodWork() === $this) { + $goal->setAccompanyingPeriodWork(null); } } @@ -375,4 +403,25 @@ use Symfony\Component\Validator\Constraints as Assert; return $this; } + + public function getPersons(): Collection + { + return $this->persons; + } + + public function addPerson(Person $person): self + { + if (!$this->persons->contains($person)) { + $this->persons[] = $person; + } + + return $this; + } + + public function removePerson(Person $person): self + { + $this->persons->removeElement($person); + + return $this; + } } diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php index 955f9e3a1..d8cfb4875 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/AccompanyingPeriodWorkGoal.php @@ -7,10 +7,17 @@ use Chill\PersonBundle\Entity\SocialWork\Result; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation as Serializer; /** * @ORM\Entity * @ORM\Table(name="chill_person_accompanying_period_work_goal") + * @Serializer\DiscriminatorMap( + * typeProperty="type", + * mapping={ + * "accompanying_period_work_goal":AccompanyingPeriodWorkGoal::class + * } + * ) */ class AccompanyingPeriodWorkGoal { @@ -18,11 +25,14 @@ class AccompanyingPeriodWorkGoal * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") + * @Serializer\Groups({"read"}) */ private $id; /** * @ORM\Column(type="text") + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"read"}) */ private $note; @@ -33,12 +43,16 @@ class AccompanyingPeriodWorkGoal /** * @ORM\ManyToOne(targetEntity=Goal::class) + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"read"}) */ private $goal; /** * @ORM\ManyToMany(targetEntity=Result::class, inversedBy="accompanyingPeriodWorkGoals") * @ORM\JoinTable(name="chill_person_accompanying_period_work_goal_result") + * @Serializer\Groups({"accompanying_period_work:edit"}) + * @Serializer\Groups({"read"}) */ private $results; @@ -71,6 +85,13 @@ class AccompanyingPeriodWorkGoal public function setAccompanyingPeriodWork(?AccompanyingPeriodWork $accompanyingPeriodWork): self { + if ($this->accompanyingPeriodWork instanceof AccompanyingPeriodWork + && $accompanyingPeriodWork !== $this->accompanyingPeriodWork + && $accompanyingPeriodWork !== null + ) { + throw new \LogicException("Change accompanying period work is not allowed"); + } + $this->accompanyingPeriodWork = $accompanyingPeriodWork; return $this; diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php index 996fcf3eb..7ad301875 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Goal.php @@ -5,10 +5,17 @@ namespace Chill\PersonBundle\Entity\SocialWork; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation as Serializer; /** * @ORM\Entity * @ORM\Table(name="chill_person_social_work_goal") + * @Serializer\DiscriminatorMap( + * typeProperty="type", + * mapping={ + * "social_work_goal":Goal::class + * } + * ) */ class Goal { @@ -16,11 +23,13 @@ class Goal * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") + * @Serializer\Groups({"read"}) */ private $id; /** * @ORM\Column(type="json") + * @Serializer\Groups({"read"}) */ private $title = []; @@ -46,6 +55,11 @@ class Goal $this->results = new ArrayCollection(); } + public function getId(): ?int + { + return $this->id; + } + public function getTitle(): array { return $this->title; diff --git a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php index 37e5c2b18..42a4c0929 100644 --- a/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php +++ b/src/Bundle/ChillPersonBundle/Entity/SocialWork/Result.php @@ -7,10 +7,17 @@ use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkGoal; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation as Serializer; /** * @ORM\Entity * @ORM\Table(name="chill_person_social_work_result") + * @Serializer\DiscriminatorMap( + * typeProperty="type", + * mapping={ + * "social_work_result":Result::class + * } + * ) */ class Result { @@ -18,11 +25,13 @@ class Result * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") + * @Serializer\Groups({"read"}) */ private $id; /** * @ORM\Column(type="json") + * @Serializer\Groups({"read"}) */ private $title = []; diff --git a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php index 49543c42d..b946f3f89 100644 --- a/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php +++ b/src/Bundle/ChillPersonBundle/Menu/AccompanyingCourseMenuBuilder.php @@ -60,6 +60,13 @@ class AccompanyingCourseMenuBuilder implements LocalMenuBuilderInterface 'accompanying_period_id' => $period->getId() ]]) ->setExtras(['order' => 30]); + + $menu->addChild($this->translator->trans('Accompanying Course Actions'), [ + 'route' => 'chill_person_accompanying_period_work_list', + 'routeParameters' => [ + 'id' => $period->getId() + ]]) + ->setExtras(['order' => 40]); } diff --git a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php index 5d455b617..872feebbe 100644 --- a/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/AccompanyingPeriod/AccompanyingPeriodWorkRepository.php @@ -3,16 +3,12 @@ namespace Chill\PersonBundle\Repository\AccompanyingPeriod; use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork; +use Chill\PersonBundle\Entity\AccompanyingPeriod; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\Persistence\ObjectRepository; -/** - * @method AccompanyingPeriodWork|null find($id, $lockMode = null, $lockVersion = null) - * @method AccompanyingPeriodWork|null findOneBy(array $criteria, array $orderBy = null) - * @method AccompanyingPeriodWork[] findAll() - * @method AccompanyingPeriodWork[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) - */ -final class AccompanyingPeriodWorkRepository +final class AccompanyingPeriodWorkRepository implements ObjectRepository { private EntityRepository $repository; @@ -20,4 +16,88 @@ final class AccompanyingPeriodWorkRepository { $this->repository = $entityManager->getRepository(AccompanyingPeriodWork::class); } + + public function find($id): ?AccompanyingPeriodWork + { + return $this->repository->find($id); + } + + public function findAll(): array + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria): ?AccompanyingPeriodWork + { + return $this->findOneBy($criteria); + } + + public function getClassName() + { + return AccompanyingPeriodWork::class; + } + + /** + * + * @return AccompanyingPeriodWork[] + */ + public function findByAccompanyingPeriod(AccompanyingPeriod $period, $orderBy = null, $limit = null, $offset = null): array + { + return $this->repository->findByAccompanyingPeriod($period, $orderBy, $limit, $offset); + } + + public function countByAccompanyingPeriod(AccompanyingPeriod $period): int + { + return $this->repository->countByAccompanyingPeriod($period); + } + + public function toDelete() + { + $qb = $this->buildQueryBySocialActionWithDescendants($action); + $qb->select('g'); + + foreach ($orderBy as $sort => $order) { + $qb->addOrderBy('g.'.$sort, $order); + } + + return $qb + ->setMaxResults($limit) + ->setFirstResult($offset) + ->getQuery() + ->getResult() + ; + } + + public function countBySocialActionWithDescendants(SocialAction $action): int + { + $qb = $this->buildQueryBySocialActionWithDescendants($action); + $qb->select('COUNT(g)'); + + return $qb + ->getQuery() + ->getSingleScalarResult() + ; + } + + protected function buildQueryBySocialActionWithDescendants(SocialAction $action): QueryBuilder + { + $actions = $action->getDescendantsWithThis(); + + $qb = $this->repository->createQueryBuilder('g'); + + $orx = $qb->expr()->orX(); + $i = 0; + foreach ($actions as $action) { + $orx->add(":action_{$i} MEMBER OF g.socialActions"); + $qb->setParameter("action_{$i}", $action); + } + $qb->where($orx); + + return $qb; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php index bd7c6a738..e6aac493f 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/GoalRepository.php @@ -3,10 +3,13 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Goal; +use Chill\PersonBundle\Entity\SocialWork\SocialAction; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\QueryBuilder; +use Doctrine\Persistence\ObjectRepository; -final class GoalRepository +final class GoalRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +17,78 @@ final class GoalRepository { $this->repository = $entityManager->getRepository(Goal::class); } + + public function find($id) + { + return $this->repository->find($id); + } + + public function findAll() + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria) + { + return $this->findOneBy($criteria); + } + + public function getClassName() + { + return Goal::class; + } + + /** + * + * @return Goal[] + */ + public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array + { + $qb = $this->buildQueryBySocialActionWithDescendants($action); + $qb->select('g'); + + foreach ($orderBy as $sort => $order) { + $qb->addOrderBy('g.'.$sort, $order); + } + + return $qb + ->setMaxResults($limit) + ->setFirstResult($offset) + ->getQuery() + ->getResult() + ; + } + + public function countBySocialActionWithDescendants(SocialAction $action): int + { + $qb = $this->buildQueryBySocialActionWithDescendants($action); + $qb->select('COUNT(g)'); + + return $qb + ->getQuery() + ->getSingleScalarResult() + ; + } + + protected function buildQueryBySocialActionWithDescendants(SocialAction $action): QueryBuilder + { + $actions = $action->getDescendantsWithThis(); + + $qb = $this->repository->createQueryBuilder('g'); + + $orx = $qb->expr()->orX(); + $i = 0; + foreach ($actions as $action) { + $orx->add(":action_{$i} MEMBER OF g.socialActions"); + $qb->setParameter("action_{$i}", $action); + } + $qb->where($orx); + + return $qb; + } } diff --git a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php index 1c4d8d013..6dc6a887c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/SocialWork/ResultRepository.php @@ -3,10 +3,14 @@ namespace Chill\PersonBundle\Repository\SocialWork; use Chill\PersonBundle\Entity\SocialWork\Result; +use Chill\PersonBundle\Entity\SocialWork\SocialAction; +use Chill\PersonBundle\Entity\SocialWork\Goal; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\QueryBuilder; +use Doctrine\Persistence\ObjectRepository; -final class ResultRepository +final class ResultRepository implements ObjectRepository { private EntityRepository $repository; @@ -14,4 +18,120 @@ final class ResultRepository { $this->repository = $entityManager->getRepository(Result::class); } + + public function find($id) + { + return $this->repository->find($id); + } + + public function findAll() + { + return $this->repository->findAll(); + } + + public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) + { + return $this->repository->findBy($criteria, $orderBy, $limit, $offset); + } + + public function findOneBy(array $criteria) + { + return $this->findOneBy($criteria); + } + + public function getClassName() + { + return Result::class; + } + + /** + * + * @return Result[] + */ + public function findBySocialActionWithDescendants(SocialAction $action, $orderBy = null, $limit = null, $offset = null): array + { + $qb = $this->buildQueryBySocialActionWithDescendants($action); + $qb->select('r'); + + foreach ($orderBy as $sort => $order) { + $qb->addOrderBy('r.'.$sort, $order); + } + + return $qb + ->setMaxResults($limit) + ->setFirstResult($offset) + ->getQuery() + ->getResult() + ; + } + + public function countBySocialActionWithDescendants(SocialAction $action): int + { + $qb = $this->buildQueryBySocialActionWithDescendants($action); + $qb->select('COUNT(r)'); + + return $qb + ->getQuery() + ->getSingleScalarResult() + ; + } + + protected function buildQueryBySocialActionWithDescendants(SocialAction $action): QueryBuilder + { + $actions = $action->getDescendantsWithThis(); + + $qb = $this->repository->createQueryBuilder('r'); + + $orx = $qb->expr()->orX(); + $i = 0; + foreach ($actions as $action) { + $orx->add(":action_{$i} MEMBER OF r.socialActions"); + $qb->setParameter("action_{$i}", $action); + } + $qb->where($orx); + + return $qb; + } + protected function buildQueryByGoal(Goal $goal): QueryBuilder + { + $qb = $this->repository->createQueryBuilder('r'); + + $qb->where(":goal MEMBER OF r.goals") + ->setParameter('goal', $goal) + ; + + return $qb; + } + + /** + * @return Result[] + */ + public function findByGoal(Goal $goal, $orderBy = null, $limit = null, $offset = null): array + { + $qb = $this->buildQueryByGoal($goal); + + foreach ($orderBy as $sort => $order) { + $qb->addOrderBy('r.'.$sort, $order); + } + + return $qb + ->select('r') + ->setMaxResults($limit) + ->setFirstResult($offset) + ->getQuery() + ->getResult() + ; + } + + public function countByGoal(Goal $goal): int + { + $qb = $this->buildQueryByGoal($goal); + $qb->select('COUNT(r)'); + + return $qb + ->getQuery() + ->getSingleScalarResult() + ; + } } + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/modules/accompanying_course_work_list/index.js b/src/Bundle/ChillPersonBundle/Resources/public/modules/accompanying_course_work_list/index.js new file mode 100644 index 000000000..cbc8531c0 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/modules/accompanying_course_work_list/index.js @@ -0,0 +1 @@ +require('./index.scss'); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/modules/accompanying_course_work_list/index.scss b/src/Bundle/ChillPersonBundle/Resources/public/modules/accompanying_course_work_list/index.scss new file mode 100644 index 000000000..433f291b5 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/modules/accompanying_course_work_list/index.scss @@ -0,0 +1,96 @@ + +#accompanying_course_work_list { + + .item { + margin-bottom: 1.5rem; + padding: 1rem; + border: 1px solid gray; + + .title_label { + display: block; + margin: 0; + + font-variant-caps: small-caps; + + + * { + margin-top: 0; + } + } + + .objective_results { + display: grid; + grid-template-areas: + "obj res" + ; + grid-template-columns: 50%; + column-gap: 1rem; + + padding: 0.3rem; + + .obj { + grid-area: obj; + } + + .res { + grid-area: res; + + ul.result_list { + list-style-type: none; + padding: 0; + } + } + } + + .objective_results:nth-child(2n+2) { + background-color: var(--chill-llight-gray); + } + } + + .updatedBy { + margin-top: 1rem; + text-align: right; + font-size: 0.8rem; + font-style: italic; + } +} + +ul.timeline { + display: flex; + align-items: center; + justify-content: center; + padding: 0; + list-style-type: none; + + > li { + min-width: 210px; + + div.date { + margin-bottom: 20px; + display: flex; + flex-direction: column; + align-items: center; + } + div.label { + display: flex; + flex-direction: column; + align-items: center; + + padding: 0 40px; + border-top: 2px solid var(--chill-green); + + &:before { + content: ''; + display: inline-block; + position: relative; + width: 25px; + height: 25px; + + background-color: white; + border-radius: 25px; + border: 1px solid #ddd; + + top: -15px; + } + } + } +} diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js index 2a7f19d61..bd47d4a73 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourse/index.js @@ -24,7 +24,6 @@ if (root === 'app') { .use(i18n) .component('app', App) .mount('#accompanying-course'); - }); } @@ -43,8 +42,7 @@ if (root === 'banner') { .use(store) .use(i18n) .component('banner', Banner) - .mount('#accompanying-course'); - + .mount('#banner-accompanying-course'); }); } diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/App.vue index 3c1f4b07a..96edce825 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/App.vue +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/App.vue @@ -13,8 +13,7 @@
-

{{ $t('pick_an_action') }}

- +

{{ $t('pick_an_action') }}

spinner

- +
+

{{ $t('persons_involved') }}

+ + +
+ +

@@ -86,6 +96,14 @@ #picking { grid-area: picking; + + #persons { + ul { + padding: 0; + + list-style-type: none; + } + } } #start_date { @@ -106,6 +124,7 @@ import { mapState, mapActions, mapGetters } from 'vuex'; import VueMultiselect from 'vue-multiselect'; import { dateToISO, ISOToDate } from 'ChillMainAssets/js/date.js'; +import Person from 'ChillPersonAssets/vuejs/_components/Person/Person.vue'; const i18n = { messages: { @@ -116,6 +135,7 @@ const i18n = { pick_social_issue: "Choisir une problématique sociale", pick_an_action: "Choisir une action d'accompagnement", pick_social_issue_linked_with_action: "Indiquez la problématique sociale liée à l'action d'accompagnement", + persons_involved: "Usagers concernés", } } @@ -125,6 +145,7 @@ export default { name: 'App', components: { VueMultiselect, + Person, }, methods: { submit() { @@ -137,6 +158,7 @@ export default { 'socialIssues', 'socialActionsReachables', 'errors', + 'personsReachables', ]), ...mapGetters([ 'hasSocialIssuePicked', @@ -145,6 +167,18 @@ export default { 'isPostingWork', 'hasErrors', ]), + personsPicked: { + get() { + let s = this.$store.state.personsPicked.map(p => p.id); + console.log('persons picked', s); + + return s; + }, + set(v) { + console.log('persons picked', v); + this.$store.commit('setPersonsPickedIds', v); + } + }, socialIssuePicked: { get() { let s = this.$store.state.socialIssuePicked; diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/store.js index 040af5097..5efe3a6e9 100644 --- a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/store.js +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkCreate/store.js @@ -14,6 +14,10 @@ const store = createStore({ socialIssuePicked: null, socialActionsReachables: [], socialActionPicked: null, + personsPicked: window.accompanyingCourse.participations.filter(p => p.endDate == null) + .map(p => p.person), + personsReachables: window.accompanyingCourse.participations.filter(p => p.endDate == null) + .map(p => p.person), startDate: new Date(), endDate: null, isLoadingSocialActions: false, @@ -38,15 +42,23 @@ const store = createStore({ buildPayloadCreate(state) { let payload = { type: 'accompanying_period_work', - social_action: { + socialAction: { type: 'social_work_social_action', id: state.socialActionPicked.id }, startDate: { datetime: datetimeToISO(state.startDate) - } + }, + persons: [] }; + for (let i in state.personsPicked) { + payload.persons.push({ + id: state.personsPicked[i].id, + type: 'person' + }); + } + if (null !== state.endDate) { payload.endDate = { datetime: datetimeToISO(state.endDate) @@ -71,12 +83,16 @@ const store = createStore({ state.socialActionPicked = socialAction; }, setSocialIssue(state, socialIssueId) { + console.log('set social issue', socialIssueId); if (socialIssueId === null) { state.socialIssuePicked = null; - return; + } else { + let mapped = state.socialIssues + .find(e => e.id === socialIssueId); + console.log('mapped', mapped); + state.socialIssuePicked = mapped; + console.log('social issue setted', state.socialIssuePicked); } - state.socialIssuePicked = state.socialIssues - .find(e => e.id === socialIssueId); }, setIsLoadingSocialActions(state, s) { state.isLoadingSocialActions = s; @@ -90,6 +106,11 @@ const store = createStore({ setEndDate(state, date) { state.endDate = date; }, + setPersonsPickedIds(state, ids) { + console.log('persons ids', ids); + state.personsPicked = state.personsReachables + .filter(p => ids.includes(p.id)) + }, addErrors(state, { errors, cancel_posting }) { console.log('add errors', errors); state.errors = errors; @@ -103,8 +124,10 @@ const store = createStore({ console.log('pick social issue'); commit('setIsLoadingSocialActions', true); - commit('setSocialIssue', null); + commit('setSocialAction', null); commit('setSocialActionsReachables', []); + commit('setSocialIssue', null); + findSocialActionsBySocialIssue(socialIssueId).then( (response) => { @@ -144,9 +167,6 @@ const store = createStore({ commit('addErrors', { errors, cancel_posting: true }); } }); - - - }, }, diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue new file mode 100644 index 000000000..15c500af2 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/App.vue @@ -0,0 +1,486 @@ + + + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/_components/AddResult.vue b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/_components/AddResult.vue new file mode 100644 index 000000000..2eb1e5b33 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/_components/AddResult.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js new file mode 100644 index 000000000..0ef2d0de4 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/index.js @@ -0,0 +1,15 @@ +import { createApp } from 'vue'; +import { _createI18n } from 'ChillMainAssets/vuejs/_js/i18n'; +import { store } from './store'; +import { personMessages } from 'ChillPersonAssets/vuejs/_js/i18n' +import App from './App.vue'; + +const i18n = _createI18n(personMessages); + +const app = createApp({ + template: ``, +}) +.use(store) +.use(i18n) +.component('app', App) +.mount('#accompanying_course_work_edit'); diff --git a/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js new file mode 100644 index 000000000..70e516153 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/public/vuejs/AccompanyingCourseWorkEdit/store.js @@ -0,0 +1,304 @@ +import { createStore } from 'vuex'; +import { datetimeToISO, ISOToDatetime } from 'ChillMainAssets/js/date.js'; +import { findSocialActionsBySocialIssue } from 'ChillPersonAssets/vuejs/_api/SocialWorkSocialAction.js'; +import { create } from 'ChillPersonAssets/vuejs/_api/AccompanyingCourseWork.js'; + +const debug = process.env.NODE_ENV !== 'production'; + +console.log(window.accompanyingCourseWork); + +const store = createStore({ + strict: debug, + state: { + work: window.accompanyingCourseWork, + startDate: ISOToDatetime(window.accompanyingCourseWork.startDate.datetime), + endDate: (window.accompanyingCourseWork.endDate !== null ? + ISOToDatetime(window.accompanyingCourseWork.endDate.datetime) : null), + note: window.accompanyingCourseWork.note, + goalsPicked: window.accompanyingCourseWork.goals, + resultsPicked: window.accompanyingCourseWork.results, + resultsForAction: [], + goalsForAction: [], + resultsForGoal: [], + personsPicked: window.accompanyingCourseWork.persons, + personsReachables: window.accompanyingCourseWork.accompanyingPeriod.participations.filter(p => p.endDate == null) + .map(p => p.person), + handlingThirdParty: window.accompanyingCourseWork.handlingThierParty, + thirdParties: window.accompanyingCourseWork.thirdParties, + isPosting: false, + errors: [], + }, + getters: { + socialAction(state) { + return state.work.socialAction; + }, + hasResultsForAction(state) { + return state.resultsForAction.length > 0; + }, + resultsForGoal: (state) => (goal) => { + let founds = state.resultsForGoal.filter(r => r.goalId === goal.id); + + return founds === undefined ? [] : founds; + }, + resultsPickedForGoal: (state) => (goal) => { + let found = state.goalsPicked.find(g => g.goal.id === goal.id); + + return found === undefined ? [] : found.results; + }, + hasHandlingThirdParty(state) { + return state.handlingThirdParty !== null; + }, + hasThirdParties(state) { + return state.thirdParties.length > 0; + }, + buildPayload(state) { + return { + type: 'accompanying_period_work', + id: state.work.id, + startDate: { + datetime: datetimeToISO(state.startDate) + }, + endDate: state.endDate === null ? null : { + datetime: datetimeToISO(state.endDate) + }, + note: state.note, + persons: state.personsPicked.map(p => ({id: p.id, type: p.type})), + handlingThierParty: state.handlingThirdParty === null ? null : { + id: state.handlingThirdParty.id, + type: state.handlingThirdParty.type + }, + results: state.resultsPicked.map(r => ({id: r.id, type: r.type})), + thirdParties: state.thirdParties.map(t => ({id: t.id, type: t.type})), + goals: state.goalsPicked.map(g => { + let o = { + type: g.type, + note: g.note, + goal: { + type: g.goal.type, + id: g.goal.id, + }, + results: g.results.map(r => ({id: r.id, type: r.type})), + }; + + if (g.id !== undefined) { + o.id = g.id; + } + + return o; + }) + }; + } + }, + mutations: { + setStartDate(state, date) { + state.startDate = date; + }, + setEndDate(state, date) { + state.endDate = date; + }, + setResultsForAction(state, results) { + console.log('set results for action', results); + state.resultsForAction = results; + }, + setResultsForGoal(state, { goal, results }) { + console.log('set results for goal', results); + state.goalsForAction.push(goal); + for (let i in results) { + let r = results[i]; + r.goalId = goal.id; + console.log('adding result', r); + state.resultsForGoal.push(r); + } + }, + addResultPicked(state, result) { + state.resultsPicked.push(result); + }, + removeResultPicked(state, result) { + state.resultsPicked = state.resultsPicked.filter(r => r.id !== result.id); + }, + addGoal(state, goal) { + let g = { + type: "accompanying_period_work_goal", + goal: goal, + note: '', + results: [] + } + state.goalsPicked.push(g); + }, + removeGoal(state, goal) { + state.goalsPicked = state.goalsPicked.filter(g => g.id !== goal.id); + }, + addResultForGoalPicked(state, { goal, result}) { + let found = state.goalsPicked.find(g => g.goal.id === goal.id); + console.log('adResultForGoalPicked'); + console.log('found', found); + console.log('goal', goal); + console.log('result', result); + + if (found === undefined) { + return; + } + + found.results.push(result); + }, + removeResultForGoalPicked(state, { goal, result}) { + let found = state.goalsPicked.find(g => g.goal.id === goal.id); + + if (found === undefined) { + return; + } + + found.results = found.results.filter(r => r.id !== result.id); + }, + setPersonsPickedIds(state, ids) { + console.log('persons ids', ids); + state.personsPicked = state.personsReachables + .filter(p => ids.includes(p.id)) + }, + setNote(state, note) { + state.note = note; + }, + setHandlingThirdParty(state, thirdParty) { + state.handlingThirdParty = thirdParty; + }, + addThirdParties(state, thirdParties) { + console.log('addThirdParties', thirdParties); + // filter to remove existing thirdparties + let ids = state.thirdParties.map(t => t.id); + let unexistings = thirdParties.filter(t => !ids.includes(t.id)); + console.log('unexisting third parties', unexistings); + for (let i in unexistings) { + state.thirdParties.push(unexistings[i]); + } + }, + removeThirdParty(state, thirdParty) { + state.thirdParties = state.thirdParties + .filter(t => t.id !== thirdParty.id); + }, + setErrors(state, errors) { + console.log('handling errors', errors); + state.errors = errors; + }, + setIsPosting(state, st) { + state.isPosting = st; + }, + }, + actions: { + getReachablesGoalsForAction({ getters, commit, dispatch }) { + console.log('getReachablesGoalsForAction'); + let + socialActionId = getters.socialAction.id, + url = `/api/1.0/person/social-work/goal/by-social-action/${socialActionId}.json` + ; + + console.log(url); + + window + .fetch( + url + ).then( response => { + if (response.ok) { + return response.json(); + } + throw { m: 'Error while retriving goal for social action', s: response.status, b: response.body }; + }).then( data => { + for (let i in data.results) { + dispatch('getReachablesResultsForGoal', data.results[i]); + } + }).catch( errors => { + commit('addErrors', errors); + }); + }, + getReachablesResultsForGoal({ commit }, goal) { + console.log('getReachablesResultsForGoal'); + let + url = `/api/1.0/person/social-work/result/by-goal/${goal.id}.json` + ; + + console.log(url); + + window.fetch(url) + .then(response => { + if (response.ok) { + return response.json(); + } + + throw { m: 'Error while retriving results for goal', s: response.status, b: response.body }; + }) + .then(data => { + console.log('data'); + commit('setResultsForGoal', { goal, results: data.results }); + }); + }, + getReachablesResultsForAction({ getters, commit }) { + console.log('getReachablesResultsForAction'); + let + socialActionId = getters.socialAction.id, + url = `/api/1.0/person/social-work/result/by-social-action/${socialActionId}.json` + ; + + console.log(url); + + window.fetch(url) + .then(response => { + if (response.ok) { + return response.json(); + } + + throw { m: 'Error while retriving results for social action', s: response.status, b: response.body }; + }) + .then(data => { + console.log('data retrived', data); + commit('setResultsForAction', data.results); + }); + }, + submit({ getters, state, commit }) { + let + payload = getters.buildPayload, + url = `/api/1.0/person/accompanying-course/work/${state.work.id}.json`, + errors = [] + ; + + console.log('action subitting', payload, url); + commit('setIsPosting', true); + + window.fetch(url, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }).then(response => { + if (response.ok || response.status === 422) { + return response.json().then(data => ({data, status: response.status})); + } + + throw new Error(response.status); + }).then(({data, status}) => { + if (status === 422) { + for (let i in data.violations) { + errors.push(data.violations[i].title); + } + commit('setErrors', errors); + commit('setIsPosting', false); + } else { + console.info('nothing to do here, bye bye'); + window.location.assign(`/fr/person/accompanying-period/${state.work.accompanyingPeriod.id}/work`); + } + }).catch(e => { + commit('setErrors', [ + 'Erreur serveur ou réseau: veuillez ré-essayer. Code erreur: ' + e + ]); + commit('setIsPosting', false); + }); + }, + initAsync({ dispatch }) { + dispatch('getReachablesResultsForAction'); + dispatch('getReachablesGoalsForAction'); + }, + } +}); + +store.dispatch('initAsync'); + +export { store }; diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig index 3c1d0dadc..aea493eff 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourse/banner.html.twig @@ -24,3 +24,4 @@ + diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/edit.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/edit.html.twig new file mode 100644 index 000000000..53314cf17 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/edit.html.twig @@ -0,0 +1,25 @@ +{% extends '@ChillPerson/AccompanyingCourse/layout.html.twig' %} + +{% block title 'accompanying_course_work.Edit accompanying course work'|trans %} + + +{% block content %} +

{{ block('title') }}

+ +
+ +{% endblock %} + +{% block js %} + {{ parent() }} + + + {{ encore_entry_script_tags('accompanying_course_work_edit') }} +{% endblock %} + +{% block css %} + {{ parent() }} + {{ encore_entry_link_tags('accompanying_course_work_edit') }} +{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig new file mode 100644 index 000000000..28626f7c2 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/Resources/views/AccompanyingCourseWork/list_by_accompanying_period.html.twig @@ -0,0 +1,124 @@ +{% extends '@ChillPerson/AccompanyingCourse/layout.html.twig' %} + +{% block title 'accompanying_course_work.List accompanying course work'|trans %} + + +{% block content %} +

{{ block('title') }}

+ +
+ {% for w in works %} +
+
+

{{ 'accompanying_course_work.action'|trans }}

+

+ {{ w.socialAction|chill_entity_render_box({ 'no-badge': true }) }} +

+
+ +
+
    +
  • +
    + {{ w.createdAt|format_date('long') }} +
    +
    + {{ 'accompanying_course_work.create_date'|trans }} +
    +
  • +
  • +
    + {{ w.startDate|format_date('long') }} +
    +
    + {{ 'accompanying_course_work.start_date'|trans }} +
    +
  • +
  • +
    + {{ w.endDate|format_date('long') }} +
    +
    + {{ 'accompanying_course_work.end_date'|trans }} +
    +
  • +
+
+ + {% if w.results|length > 0 %} +
+
+

{{ 'accompanying_course_work.goal'|trans }}

+

{{ 'accompanying_course_work.results without objective'|trans }}

+
+
+

{{ 'accompanying_course_work.results'|trans }}

+
    + {% for r in w.results %} +
  • {{ r.title|localize_translatable_string }}
  • + {% endfor %} +
+
+
+ {% endif %} + + {% if w.goals|length > 0 %} + {% for g in w.goals %} +
+
+

{{ 'accompanying_course_work.goal'|trans }}

+

{{ g.goal.title|localize_translatable_string }}

+
+
+ {% if g.results|length == 0 %} +

{{ 'accompanying_course_work.results'|trans }}

+

{{ 'accompanying_course_work.no_results'|trans }}

+ {% else %} +

{{ 'accompanying_course_work.results'|trans }}

+
    + {% for r in g.results %} +
  • {{ r.title|localize_translatable_string }}
  • + {% endfor %} +
+ {% endif %} +
+
+ {% endfor %} + {% endif %} + +
+ {{ 'Last updated by'|trans}}: {{ w.updatedBy|chill_entity_render_box }}, {{ w.updatedAt|format_datetime('long', 'short') }} +
+ + +
+ +
+
+ {% else %} +

{{ 'accompanying_course_work.No work'|trans }}

+ {% endfor %} +
+ + + +{% endblock %} + + +{% block css %} + {{ parent() }} + {{ encore_entry_link_tags('accompanying_course_work_list') }} +{% endblock %} diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Entity/social_action.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Entity/social_action.html.twig index 246ddd1ea..858643e95 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Entity/social_action.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Entity/social_action.html.twig @@ -1,6 +1,6 @@ {% set reversed_parents = parents|reverse %} - + {%- for p in reversed_parents %}